4

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.

2
+50

This 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.

  • None of your proposed solutions would work, because (1) the REST API sets a set of custom headers internally, and setting headers on own scripts will make it to fail; and (2) deploying AJAX requests would require changing from SSE to needing to define a whole new endpoint for Telegram to send its data to the server, and worse, the need of query new messages via long polling. – acidrums4 Oct 15 '18 at 16:38
0

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.

  • Thanks. Just tried it but it was no use - Still got the 'EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection.' message. Although I was expecting that, 'cause I guess this would do pretty much the same that setting headers in WP_REST_Response. – acidrums4 Oct 18 '18 at 17:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.