2016-01-20 11 views
12

Sto lavorando con il comando lasco (codice Python è in esecuzione dietro questo), funziona benissimo, ma questo dà l'erroreCome evitare l'errore di timeout del comando slack?

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

Come evitare questo?

risposta

-10

Creare thread per eseguire gran parte del lavoro e restituire la risposta al gioco. Puoi anche restituire una risposta al completamento del thread.

+0

Per spiegare eventualmente i downvotes su questa risposta (auto-accettata?): questa risposta non risolve * impedendo * i timeout; il nucleo della tua domanda originale. La vera risposta/soluzione è che dovresti restituire una risposta a Slack immediatamente dopo aver ricevuto la richiesta, e 'POST' la vera risposta (una volta che hai) al parametro' response_url' come nella richiesta originale. –

22

In base allo Slack slash command documentation, è necessario rispondere entro 3000 ms (tre secondi). Se il tuo comando richiede più tempo, ottieni l'errore Timeout was reached. Il tuo codice ovviamente non smetterà di funzionare, ma l'utente non riceverà alcuna risposta al loro comando.

Tre secondi vanno bene per una cosa rapida in cui il comando ha accesso immediato ai dati, ma potrebbe non essere abbastanza lungo se si sta chiamando a API esterne o facendo qualcosa di complicato. Se fai necessità di richiedere più tempo, quindi vedere le risposte ritardate e le risposte multiple sezione della documentazione:

  1. convalidare la richiesta è a posto.
  2. restituire una risposta 200 immediatamente, forse qualcosa sulla falsariga di {'text': 'ok, got that'}
  3. Go ed eseguire l'azione reale che si vuole fare.
  4. Nella richiesta originale, si ottiene un parametro univoco response_url. Fare una richiesta POST a tale URL con il follow-up messaggio:
    • Content-type deve essere application/json
    • Con il corpo come un messaggio JSON-encoded: {'text': 'all done :)'}
    • si può tornare risposte effimere o in canali , e aggiungere allegati lo stesso come l'approccio immediato

Secondo la documentazione ", è possibile rispondere ai comandi di un utente fino a 5 volte entro 30 minuti s dell'invocazione dell'utente ".

+0

sto usando semplicemente la dichiarazione di ritorno per la risposta di invio all'utente, Come inviare più risposte in python? –

+0

@abc è necessario rendere il tuo codice più complicato purtroppo - magari tramite qualcosa come [Celery] (http://www.celeryproject.org). Quale server web/framework/ambiente stai usando? – rcoup

+0

@rcoup hai qualche idea di cosa mi manca qui? http://stackoverflow.com/questions/36195924/slack-php-api-avoid-timeout-error – Vimalnath

4

anch'io stavo affrontando questo errore frequente:

"Darn - che Slash comando non ha funzionato (messaggio di errore: Timeout was reached) Gestire il comando a barra-comando."

I stava scrivendo un Slack slash-command "bot" on AWS Lambda che a volte era necessario per eseguire operazioni lente (invocando altre API esterne, ecc.). In alcuni casi, la funzione Lambda potrebbe richiedere più di 3 secondi, causando l'errore Timeout was reached da Slack.

Ho trovato l'eccellente risposta di @ rcoup qui e l'ho applicata nel contesto di AWS Lambda. L'errore non appare più.

L'ho fatto con due funzioni Lambda separate. Uno è un "dispatcher" o "receptionist" che saluta il comando Slack Slack in entrata con un "200 OK" e restituisce all'utente il semplice tipo di messaggio "Ok, ha ottenuto".L'altra è la funzione Lambda "worker" effettiva che avvia l'operazione long-ish in modo asincrono e registra il risultato di tale operazione su Slack response_url in seguito.

Questo è il dispatcher/receptionist funzione 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 

Come si può vedere da quanto sopra, non ha invocato il lavoratore Lambda funzione direttamente dal dispatcher (anche se questo è possibile). Ho scelto lo use AWS SNS to publish a message that the worker receives and processes.

Basato su this StackOverflow answer, questo è l'approccio migliore in quanto è non bloccante (asincrono) e scalabile. Inoltre è stato più semplice usare SNS per disaccoppiare le due funzioni nel contesto di AWS Lambda, l'invocazione diretta è più complicata per questo caso d'uso.

Infine, ecco come ho consumare l'evento SNS nel mio operaio Lambda Funzione:

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` 
2

Dopo che fare con questo problema me stesso e avere il mio Flask applicazione ospitata su Heroku ho scoperto che la soluzione più semplice era quella di utilizzare threading . Ho seguito l'esempio da qui: 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") 

Tutto il lavoro pesante lento viene eseguito dalla funzione backgroundworker(). Il comando slack punta a https://myappaddress.com/appmethodaddress dove la funzione receptionist() prende il numero response_url del messaggio Slack ricevuto e lo passa accanto a qualsiasi altro dato facoltativo a backgroundworker(). Poiché il processo è ora suddiviso, restituisce semplicemente il messaggio "working on your request" al canale Slack praticamente all'istante e al completamento, backgroundworker(), invia il secondo messaggio "your task is complete".

Problemi correlati