From f4efe4aefcf15dcc6b6527015029554b601829bc Mon Sep 17 00:00:00 2001 From: groug Date: Sun, 22 Oct 2023 15:11:01 +0200 Subject: [PATCH] events processing changed: treat events chronologically (except for the first time, to avoid looping throug all history), phpdoc, custom update period --- example.php | 1 + phpmatrix.php | 145 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 119 insertions(+), 27 deletions(-) diff --git a/example.php b/example.php index 0c92752..09495ee 100644 --- a/example.php +++ b/example.php @@ -25,6 +25,7 @@ $cb = function($event) $m = new MatrixClient("https://domain.tld"); //$m->login_with_password("@bot:domain.tld", "password"); //echo $m->get_access_token() . "\n"; +$m->set_update_period(0.5); $m->login_with_token("@bot:domain.tld", "token"); $room = $m->join_room("!roomname:domain.tld"); //$room->send_text("pouet"); diff --git a/phpmatrix.php b/phpmatrix.php index 256b7c8..7311b7d 100644 --- a/phpmatrix.php +++ b/phpmatrix.php @@ -2,12 +2,24 @@ class MatrixClient { + /** @var string Matrix server (https://domain.tld) */ private $matrix_server; - private $rooms; - private $transaction_id; + /** @var string MXID (@login:domain.tld) */ private $mxid; - private $access_token; + /** @var MatrixRoom[] List of subscribed rooms */ + private $rooms; + + /** @var int API transaction ID */ + private $transaction_id; + /** @var string API Access token */ + private $access_token; + /** @var int Update period in microseconds */ + private $update_period = 1000000; + + /** + * @param string $matrix_server Matrix server (https://domain.tld) + */ function __construct($matrix_server) { $this->matrix_server = $matrix_server; @@ -17,23 +29,47 @@ class MatrixClient $this->transaction_id = microtime(); } + /** + * @return int + */ private function get_new_transaction_id() { $this->transaction_id = microtime(); return $this->transaction_id; } + /** + * @return string + */ public function get_access_token() { return $this->access_token; } + /** + * @return string + */ public function get_mxid() { return $this->mxid; } - function login_with_password($mxid, $password) + /** + * Change update period (for events update) + * @param float $seconds Period in seconds + */ + public function set_update_period($seconds) + { + $this->update_period = intval($seconds * 1000000); + } + + /** + * Try to login to Matrix API and get an access token. + * Will throw a MatrixRequestException if it fails + * @param string $mxid client's login (@login:domain.tld) + * @param string $password client's password + */ + public function login_with_password($mxid, $password) { $this->mxid = $mxid; @@ -54,13 +90,17 @@ class MatrixClient } /** - * @var string $url_path: path after the TLD - * @var string $method: GET, PUT, POST - * @var string[] $body: array of key/values for POST/PUT - * @var string[] $params: URL params - * @var bool $require_token: if true, access_token needed + * Query to the Matrix API + * Will throw a MatrixRequestException if it fails + * TODO: change default timeouts? + * @param string $url_path: path after the Matrix server + * @param string $method: GET, PUT, POST + * @param string[] $body: array of key/values for POST/PUT + * @param string[] $params: URL params + * @param bool $require_token: if true, access_token needed + * @return string[] json decoded response */ - function query($url_path, $method="GET", $body=null, $params=null, $require_token=true) + public function query($url_path, $method="GET", $body=null, $params=null, $require_token=true) { if (!$this->access_token && $require_token) { throw new MatrixRequestException("Missing access token", 0); @@ -111,7 +151,14 @@ class MatrixClient return $res; } - function send_room_event($room_id, $event_type, $body) + /** + * Send a room event + * @param string $room_id Room ID (!xxxxxx:domain.tld. #alias:domain.tld seems to fail) + * @param string $event_type Event type (e.g. m.room.message) + * @param string[] $body key-value array + * @return string[] json decoded response + */ + public function send_room_event($room_id, $event_type, $body) { $txn_id = $this->get_new_transaction_id(); $room_id = curl_escape($this->ch, $room_id); @@ -120,20 +167,24 @@ class MatrixClient } /** - * @var string $mxid: mxid (@xxxx:yyyy.zz) to identify the user - * @var string $access_token: the access token returned by a login + * Will just store the mxid/access token + * TODO: check if token valid? + * @param string $mxid: mxid (@xxxx:yyyy.zz) to identify the user + * @param string $access_token: the access token returned by a login */ - function login_with_token($mxid, $access_token) + public function login_with_token($mxid, $access_token) { $this->mxid = $mxid; $this->access_token = $access_token; } /** - * @var string $room_id room id or alias (#xxxx:yyy.zz) + * Subscribe to a room: will allow to receive/send events from/to this room + * NB: the user has to have already join the room + * @param string $room_id room id or alias (#xxxx:yyy.zz) * @return MatrixRoom */ - function join_room($room_id) + public function join_room($room_id) { $room = new MatrixRoom($this, $room_id); $this->rooms[$room_id] = $room; @@ -141,13 +192,16 @@ class MatrixClient return $room; } - function run() + /** + * Blocking loop to get rooms' events + */ + public function run() { while (true) { foreach ($this->rooms as $room) { $room->get_events(); } - usleep(500000); + usleep($this->update_period); } } } @@ -171,6 +225,10 @@ class MatrixEvent public $user_id; public $age; + /** + * @param MatrixRoom $room the room that received the event + * @param string[] $contents the event's contents + */ function __construct($room, $contents) { $this->room = $room; @@ -178,6 +236,7 @@ class MatrixEvent $this->sender = $contents["sender"]; $this->content = $contents["content"]; $this->event_id = $contents["event_id"]; + // TODO: convert to datetime? $this->origin_server_ts = $contents["origin_server_ts"]; $this->user_id = $contents["user_id"]; $this->age = isset($contents["age"]) ? $contents["age"] : null; @@ -191,6 +250,10 @@ class MatrixRoom private $listeners = []; private $last_end_token = null; + /** + * @param MatrixClient $client + * @param string $room_id + */ function __construct($client, $room_id) { $this->client = $client; @@ -198,14 +261,23 @@ class MatrixRoom } /** - * @var $callback function(room, event) + * Add an event listener: will be called each time we received a room's event + * NB: logged-in user's events are ignored + * TODO: param to allow them? + * @param $callback function(room, event) */ - function add_listener($callback) + public function add_listener($callback) { $this->listeners[] = $callback; } - function send_text($text) + /** + * Helper to send a simple text message (m.room.message with a text body) + * Will throw a MatrixRequest Exception if it fails + * @param string $text the raw text + * @return string[] json decoded response + */ + public function send_text($text) { $body = [ "body" => $text, @@ -214,7 +286,14 @@ class MatrixRoom return $this->client->send_room_event($this->room_id, "m.room.message", $body); } - function send_html($html) + /** + * Helper to send a simple html message (m.room.message with html body) + * TODO: text body too? (RTFM) + * Will throw a MatrixRequest Exception if it fails + * @param string $html the HTML + * @return string[] json decoded response + */ + public function send_html($html) { $body = [ "body" => $text, @@ -223,15 +302,23 @@ class MatrixRoom return $this->client->send_room_event($this->room_id, "m.room.message", $body); } - function get_events() + /** + * Read the room's last X events + * TODO: param to change the limit + */ + public function get_events() { $params = [ "limit" => 10, - "dir" => "b", + "dir" => $this->last_end_token ? "f" : "b", ]; + // (a bit dirty) trick: + // First time: get events in reverse order (most recent first) to get the last token (start) + // After that: get events chronologically (oldest first) with the end token + if ($this->last_end_token) { - $params["to"] = $this->last_end_token; + $params["from"] = $this->last_end_token; } $room_id = curl_escape($this->client->ch, $this->room_id); @@ -240,9 +327,13 @@ class MatrixRoom "GET", null, $params); $dispatch_events = $this->last_end_token !== null; - $this->last_end_token = $res["start"]; + if (!$this->last_end_token) { + $this->last_end_token = $res["start"]; + } elseif (isset($res["end"])) { + $this->last_end_token = $res["end"]; + } - // First time we get events: do nothing + // First time we get events : do nothing (it's history) if ($dispatch_events) { foreach ($res["chunk"] as $event) { foreach ($this->listeners as $listener) {