私はスラックコマンドで作業しています(Pythonコードはこの背後で実行されています)、それはうまく動作しますが、これはエラーを与えます
This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).
これを避ける方法は?
Slack slash command documentation によると、3000ms(3秒)以内に応答する必要があります。コマンドに時間がかかると、Timeout was reached
エラーが発生します。コードの実行は明らかに停止しませんが、ユーザーはコマンドに対する応答を受け取りません。
コマンドがデータに瞬時にアクセスする場合は3秒で十分ですが、外部APIを呼び出す場合や複雑な操作を行う場合は長さが足りない場合があります。 do時間がかかる場合は、遅延応答と複数応答ドキュメントのセクション:
200
応答を返します。おそらく{'text': 'ok, got that'}
の行に沿って何かresponse_url
パラメーターが渡されます。フォローアップメッセージを使用して、そのURLにPOST
リクエストを行います。Content-type
はapplication/json
である必要があります{'text': 'all done :)'}
ドキュメントによると、「ユーザーの呼び出しから30分以内にユーザーコマンドに最大5回応答できます」。
自分でこの問題に対処し、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()
関数によって実行されます。私のスラックコマンドはhttps://myappaddress.com/appmethodaddress
を指し、receptionist()
関数は受信したSlackメッセージのresponse_url
を受け取り、他のオプションデータと一緒にbackgroundworker()
に渡します。プロセスが分割されると、すぐに"working on your request"
メッセージがSlackチャネルに返され、完了時にbackgroundworker()
が2番目のメッセージ"your task is complete"
を送信します。
私もこのエラーに頻繁に直面していました。
「くそ–そのスラッシュコマンドは機能しませんでした(エラーメッセージ:
Timeout was reached
)。スラッシュコマンドでコマンドを管理してください。」
私は AWS Lambdaのスラックスラッシュコマンド「ボット」 を書いていましたが、これは時々遅い操作(他の外部APIを呼び出すなど)を実行するために必要でした。 Lambda関数は、場合によってはSlackからTimeout was reached
エラーを引き起こす3秒以上かかります。
ここで@rcoupの優れた答えを見つけ、AWS Lambdaのコンテキストで適用しました。エラーは表示されなくなりました。
2つの別個のLambda関数を使用してこれを行いました。 1つは、「ディスパッチャ」または「受付係」で、着信Slackスラッシュコマンドに「200 OK」で挨拶し、単純な「Ok、got that」タイプのメッセージをユーザーに返します。もう1つは、ロングオペレーションを非同期的に開始し、そのオペレーションの結果を後でSlack response_url
にポストする実際の「ワーカー」Lambda関数です。
これは、ディスパッチャ/レセプショニストの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
上記からわかるように、ディスパッチャからワーカーLambda関数を直接呼び出しませんでした(これは可能ですが)。 AWS SNSを使用して、ワーカーが受信して処理するメッセージを発行する を選択しました。
このStackOverflowの答え に基づくと、これはノンブロッキング(非同期)でスケーラブルであるため、より良いアプローチです。また、AWS LambdaのコンテキストでSNSを使用して2つの機能を分離する方が簡単でした。このユースケースでは、直接呼び出しは難しいです。
最後に、ワーカーLambda Functionで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`