Я пытаюсь использовать команду Slack Custom и не совсем уверен, как использовать отложенные сообщения, так как Йода говорят Внешний API требует более 3 секунд, чтобы ответить.
Я сделал следующее:
/Yoda
в моем случае и получил reponse_url
,post
следующий к ответу URL.$data_string = '{"response_type": "in_channel", "text":"Checking,please wait..."}' ; $chs = curl_init(); curl_setopt($chs, CURLOPT_URL, $response_url); curl_setopt($chs, CURLOPT_POST, true); curl_setopt($chs, CURLOPT_POSTFIELDS, $data_string); curl_setopt($chs, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($chs, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($chs, CURLOPT_RETURNTRANSFER, true); curl_setopt($chs, CURLOPT_POST, 1); curl_setopt($chs, CURLOPT_HTTPHEADER, array('Content-Type:application/json')); $results = curl_exec($chs);
$chsres = curl_init(); curl_setopt($chsres, CURLOPT_URL, "https://yoda.p.mashape.com/yoda?sentence=welcome+to+stack"); curl_setopt($chsres, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($chsres, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($chsres, CURLOPT_VERBOSE, true); curl_setopt($chsres, CURLOPT_TIMEOUT, 45); curl_setopt($chsres, CURLOPT_RETURNTRANSFER, true); curl_setopt($chsres, CURLOPT_HTTPHEADER, array('Content-Type:application/json', "X-Mashape-Key:> deMeGoBfMvmshQSemozTqJEY9z0jp1eIhuAjsnx9cQAQsHUifD")); $resultchsres = curl_exec($chsres); echo $resultchsres;
Может кто-нибудь, пожалуйста, дайте мне знать, как избавиться от ошибки тайм-аута, используя отложенные ответы?
ОБНОВЛЕННЫЙ КОД:
$response_url = $_POST['response_url'];
$text = $_POST['text'];
$term = str_replace(' ', '+', $text);
//https://paypal.slack.com/services/B0VQMHX8W#service_setup
//initial respond with 200OK for timeout
ignore_user_abort(true);
set_time_limit(0);
ob_start();
echo('{"response_type": "in_channel", "text": "Checking, please wait..."}');
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();$chsres = curl_init();
curl_setopt_array($chsres, array(
CURLOPT_URL => "https://yoda.p.mashape.com/yoda?sentence=$term",
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_VERBOSE => true,
CURLOPT_RETURNTRANSFER => FALSE,
CURLOPT_HTTPHEADER => array('Content-Type:application/json', "X-Mashape-Key: deMeGoBfMvmshQSemozTqJEY9z0jp1eIhuAjsnx9cQAQsHUifD"),
CURLOPT_RETURNTRANSFER => true
));
$yodaresponse = curl_exec($chsres);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $response_url,
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $yodaresponse
));
$resp = curl_exec($curl);
var_dump($resp);
curl_close($curl);
Я все еще получаю ту же ошибку «Черт — эта команда косая черта не работает (сообщение об ошибке: Timeout was reached
). Управлять командой в slash-command
Вы делаете все правильно, просто нужно изменить порядок.
Ответить на исходный запрос с 200 OK
ответ немедленно. Увидеть этот ответ для деталей, но по существу:
ignore_user_abort(true);
ob_start();
echo('{"response_type": "in_channel", "text": "Checking, please wait..."}');
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
затем сделайте запрос Yoda API, используя curl, как вы делаете
$response_url
используя curl, как вы делаете.Если вы используете FPM, то это то, что вы хотите — http://php.net/manual/en/function.fastcgi-finish-request.php
Ваш код будет выглядеть так …
<?php
$response_url = $_POST["response_url"];
$term = rawurlencode($_POST["text"]);
error_log("POST: " . print_r($_POST, 1));
$response = ["response_type"=>"in_channel", "text"=>"Checking, please wait..."];
echo json_encode($response);
header("Content-Type: application/json");
fastcgi_finish_request();
$ch = curl_init();
...
Из того, что я вижу в документации, вы делаете вещи в основном правильно. Просто повторяя что-либо, вы уже передаете сообщение 200 OK, поэтому нет необходимости делать это явно. Вы должны убедиться, что это не проблема сервера; действительно ли URL публикуется? Не попадаешься по пути правила переписывания?
Я внес некоторые изменения в ваш код ниже, в том числе отладку, которая пойдет в ваш журнал ошибок (т.е. журнал ошибок Apache, по умолчанию.) Попробуйте, и, по крайней мере, у вас будет больше деталей отладки.
<?php
$response_url = $_POST["response_url"];
$term = rawurlencode($_POST["text"]);
error_log("POST: " . print_r($_POST, 1));
ob_end_clean();
ob_start();
$response = ["response_type"=>"in_channel", "text"=>"Checking, please wait..."];
echo json_encode($response);
header("Content-Type: application/json");
header("Content-Length: " . ob_get_size());
ob_end_flush();
flush();
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://yoda.p.mashape.com/yoda?sentence=$term",
CURLOPT_HTTPHEADER => ["X-Mashape-Key: deMeGoBfMvmshQSemozTqJEY9z0jp1eIhuAjsnx9cQAQsHUifD"],
CURLOPT_RETURNTRANSFER => true
]);
$yodaresponse = curl_exec($ch);
curl_close($ch);
error_log("Yoda response: $yodaresponse");
$yodajson = json_encode([
"response_type"=>"in_channel",
"text"=>$yodaresponse
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $response_url,
CURLOPT_POST => 1,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $yodajson
]);
$resp = curl_exec($ch);
curl_close($ch);
error_log("API response: $resp");
Другой подход, который будет работать, — использовать запрос curl с коротким тайм-аутом для запуска второго скрипта PHP. Так как мой провайдер наложил некоторые ограничения на мою среду PHP (например, отсутствие процессов), это был единственный подход, который сработал для меня.
Первый сценарий завершится вскоре после этого и отправит HTTP OK обратно в Slack. Второй сценарий будет продолжать выполняться, обрабатывать трудоемкую обработку (например, вызывая внешние API) и, наконец, отправлять результат в виде отложенного ответа на response_url
,
1-й сценарий
Это запрос curl в вашем первом скрипте:
<?php>
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "second.php?redirect_url=$redirect_url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 400); //just some very short timeout
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
curl_exec($ch);
curl_close($ch);
/* send short response back to user, e.g. "Processing your request..." */
?>
Длина таймаута произвольна, однако в моих тестах очень короткий тайм-аут (например, 10 мс) не работал.
Вам также нужно будет реализовать способ передачи входных данных между двумя сценариями, как показано с передачей request_url
в качестве параметра URL.
Наконец, для команд слэша Slack требует, чтобы вы отправили короткий ответ пользователю.
2-й сценарий
Вот как выглядит ваш второй скрипт:
<?php
ignore_user_abort(true); //very important!
usleep (500000); //to ensure 2nd script responds after 1st
/* call external API */
/* send response back to Slack using response_url */
?>
Заявление ignore_user_abort(true);
обязательно, чтобы ваш второй скрипт продолжал работать после тайм-аута curl.
usleep
с 0,5 секунд, чтобы гарантировать, что второй сценарий отвечает после первого, но не обязательно для этого решения для работы.
Пример основан на одном ответеПродолжить выполнение PHP после отправки HTTP-ответавопрос.
Публикация ответа, так как у меня недостаточно репутации, чтобы оставлять комментарии …
У меня была такая же проблема, и затем я понял, что Slack по-разному относится к запросам и ответам. В частности, HTTP-запрос и ответ отличаются в первой строке.
Пример HTTP-запроса:
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Пример ответа HTTP:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: 11
Content-Type: text/xml
Connection: Closed
hello there
Если вы можете получить доступ к необработанным байтам для отправки в PHP (никогда не использовал PHP, поэтому не знакомы), просто сделайте его похожим на ответ, а не на запрос. В противном случае отправьте ответ немедленно, затем выполните ту работу, которая вам нужна, и отправьте запрос с новым сообщением. Это можно сделать несколькими способами, один из которых был описан @ miken32, я отказался от вызова фонового процесса в python.