So in this quest to build a custom pseudo web chat service with Telegram, I'm trying to get the messages Telegram sends to the server and redirect them to frontend. SSE with HTML5 EventSource seems the best and easy solution to do that.
The thing is that EventSource needs 'Content-Type: text/event-stream'
and 'Cache-Control: no-cache'
headers set in server's response.
If a WP_REST_REQUEST
is returned on the function that sets the response ("set_telegram_response", below), nothing will arrive at EventSource. But if the response is echoed, EventSource will close the connection claiming that the server is sending a JSON response instead of a text/event-stream
one.
Following is the barebones of the class I wrote for this. If I pull a GET request on that endpoint, it will return the "Some text" string. But the EventSource script prints nothing, as if the response were empty.
class Chat
{
private static $token, $telegram, $chat_id;
public $telegram_message;
public function __construct()
{
self::$chat_id = "<TELEGRAM-CHAT-ID>";
self::$token = "<TELEGRAM-TOKEN>";
self::$telegram = ":443/bot" . self::$token;
add_action('rest_api_init', array( $this, 'set_telegram_message_endpoint' ));
add_action('admin_post_chat_form', array( $this, 'chat_telegram' ));
add_action('admin_post_nopriv_chat_form', array( $this, 'chat_telegram' ));
}
public function set_telegram_message_endpoint()
{
register_rest_route('mybot/v2', 'bot', array(
array(
'methods' => WP_REST_SERVER::CREATABLE,
'callback' => array( $this, 'get_telegram_message' ),
),
array(
'methods' => WP_REST_SERVER::READABLE,
'callback' => array( $this, 'set_telegram_message' ),
),
));
}
public function set_telegram_message( WP_REST_REQUEST $request )
{
$new_response = new WP_REST_Response( "Some text" . PHP_EOL . PHP_EOL, 200 );
$new_response->header( 'Content-Type', 'text/event-stream' );
$new_response->header( 'Cache-Control', 'no-cache' );
ob_flush();
return $new_response;
}
public function get_telegram_message( WP_REST_REQUEST $request )
{
$this->telegram_message = $request;
//return rest_ensure_response( $request['message']['text'] );
}
public function chat_telegram( $input = null )
{
$mensaje = $input === '' ? $_POST['texto'] : $input;
echo $mensaje;
$query = http_build_query([
'chat_id' => self::$chat_id,
'text' => $mensaje,
'parse_mode' => "Markdown",
]);
$response = file_get_contents( self::$telegram . '/sendMessage?' . $query );
return $response;
}
}
I spent all the afternoon and part of this morning reading the documentation on REST API, but couldn't find any clue on what could be wrong or how to do this.
BTW, the server I'm deploying this can handle EventSource requests - I just tried it on a test PHP script and it worked well. So I really don't know what is going on here. Any help would be hugely appreciated.
So in this quest to build a custom pseudo web chat service with Telegram, I'm trying to get the messages Telegram sends to the server and redirect them to frontend. SSE with HTML5 EventSource seems the best and easy solution to do that.
The thing is that EventSource needs 'Content-Type: text/event-stream'
and 'Cache-Control: no-cache'
headers set in server's response.
If a WP_REST_REQUEST
is returned on the function that sets the response ("set_telegram_response", below), nothing will arrive at EventSource. But if the response is echoed, EventSource will close the connection claiming that the server is sending a JSON response instead of a text/event-stream
one.
Following is the barebones of the class I wrote for this. If I pull a GET request on that endpoint, it will return the "Some text" string. But the EventSource script prints nothing, as if the response were empty.
class Chat
{
private static $token, $telegram, $chat_id;
public $telegram_message;
public function __construct()
{
self::$chat_id = "<TELEGRAM-CHAT-ID>";
self::$token = "<TELEGRAM-TOKEN>";
self::$telegram = "https://api.telegram.org:443/bot" . self::$token;
add_action('rest_api_init', array( $this, 'set_telegram_message_endpoint' ));
add_action('admin_post_chat_form', array( $this, 'chat_telegram' ));
add_action('admin_post_nopriv_chat_form', array( $this, 'chat_telegram' ));
}
public function set_telegram_message_endpoint()
{
register_rest_route('mybot/v2', 'bot', array(
array(
'methods' => WP_REST_SERVER::CREATABLE,
'callback' => array( $this, 'get_telegram_message' ),
),
array(
'methods' => WP_REST_SERVER::READABLE,
'callback' => array( $this, 'set_telegram_message' ),
),
));
}
public function set_telegram_message( WP_REST_REQUEST $request )
{
$new_response = new WP_REST_Response( "Some text" . PHP_EOL . PHP_EOL, 200 );
$new_response->header( 'Content-Type', 'text/event-stream' );
$new_response->header( 'Cache-Control', 'no-cache' );
ob_flush();
return $new_response;
}
public function get_telegram_message( WP_REST_REQUEST $request )
{
$this->telegram_message = $request;
//return rest_ensure_response( $request['message']['text'] );
}
public function chat_telegram( $input = null )
{
$mensaje = $input === '' ? $_POST['texto'] : $input;
echo $mensaje;
$query = http_build_query([
'chat_id' => self::$chat_id,
'text' => $mensaje,
'parse_mode' => "Markdown",
]);
$response = file_get_contents( self::$telegram . '/sendMessage?' . $query );
return $response;
}
}
I spent all the afternoon and part of this morning reading the documentation on REST API, but couldn't find any clue on what could be wrong or how to do this.
BTW, the server I'm deploying this can handle EventSource requests - I just tried it on a test PHP script and it worked well. So I really don't know what is going on here. Any help would be hugely appreciated.
Share Improve this question edited Oct 13, 2018 at 13:11 acidrums4 asked Oct 13, 2018 at 13:05 acidrums4acidrums4 411 silver badge5 bronze badges 03 Answers
Reset to default 2 +50This is because the rest API internally uses json_encode()
to output the data. There are 2 ways that you can resolve this.
1. Prevent the API from sending the data
This might be a bit odd and raise some issues, but you can set the header type and echo the content before returning the data:
header( 'Content-Type: text/event-stream' );
header( 'Cache-Control: no-cache' );
// Echo whatever data you got here
// Prevent the API from continuing
exit();
Since this is not the standard way to use the API, it might cause some issues. I haven't tried this myself.
2. Use admin-ajax.php
Instead of the rest API, use the admin AJAX. The implementation is the same. Basically if you use json_encode()
alongside with die();
you'll get the same results as rest API. The codex page on this topic covers almost everything. By using admin AJAX, you can fully control the headers and output type.
in your custom end point (https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/) you can use this part of code for change headers for the response
$response = new WP_REST_Response($data, 200);
$response->header( 'Content-Type', 'text/event-stream' );
$response->header( 'Cache-Control', 'no-cache' );
$response->header( 'Access-Control-Allow-Origin', '*' ); //(this if you need to connect on the externale resource)
return $response;
I am not able to test this, but can you please try altering the response headers using this hook:
add_action( 'rest_api_init', function() {
remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
add_filter( 'rest_pre_serve_request', function( $value ) {
header( 'Content-Type', 'text/event-stream' );
header( 'Cache-Control', 'no-cache' );
return $value;
});
}, 15 );
I got the idea from here.