matrix-php-sdk/src/MatrixHttpApi.php

1456 lines
48 KiB
PHP
Raw Normal View History

2018-11-27 13:30:54 +01:00
<?php
namespace Aryess\PhpMatrixSdk;
2018-11-28 09:57:53 +01:00
use Aryess\PhpMatrixSdk\Exceptions\MatrixException;
use Aryess\PhpMatrixSdk\Exceptions\MatrixHttpLibException;
use Aryess\PhpMatrixSdk\Exceptions\MatrixRequestException;
2018-11-30 00:23:48 +01:00
use Aryess\PhpMatrixSdk\Exceptions\ValidationException;
2018-11-28 09:57:53 +01:00
use GuzzleHttp\Client;
2018-11-28 13:25:06 +01:00
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
2018-11-28 09:57:53 +01:00
/**
* Contains all raw Matrix HTTP Client-Server API calls.
* For room and sync handling, consider using MatrixClient.
*
* Examples:
* Create a client and send a message::
*
* $matrix = new MatrixHttpApi("https://matrix.org", $token="foobar");
* $response = $matrix.sync();
* $response = $matrix->sendMessage("!roomid:matrix.org", "Hello!");
*
2021-05-31 04:32:41 +02:00
* @see https://matrix.org/docs/spec/client_server/latest
*
2018-11-28 09:57:53 +01:00
* @package Aryess\PhpMatrixSdk
*/
class MatrixHttpApi {
2018-12-01 01:47:23 +01:00
const MATRIX_V2_API_PATH = '/_matrix/client/r0';
2018-11-30 00:23:48 +01:00
const MATRIX_V2_MEDIA_PATH = '/_matrix/media/r0';
2018-11-28 13:25:06 +01:00
const VERSION = '0.0.1-dev';
2018-11-28 09:57:53 +01:00
/**
* @var string
*/
private $baseUrl;
/**
* @var string|null
*/
private $token;
/**
* @var string|null
*/
private $identity;
/**
* @var int
*/
private $default429WaitMs;
/**
* @var bool
*/
private $useAuthorizationHeader;
/**
* @var int
*/
private $txnId;
/**
* @var bool
*/
private $validateCert;
2018-11-28 09:57:53 +01:00
/**
* @var Client
*/
private $client;
/**
* MatrixHttpApi constructor.
*
* @param string $baseUrl The home server URL e.g. 'http://localhost:8008'
* @param string|null $token Optional. The client's access token.
* @param string|null $identity Optional. The mxid to act as (For application services only).
* @param int $default429WaitMs Optional. Time in milliseconds to wait before retrying a request
* when server returns a HTTP 429 response without a 'retry_after_ms' key.
* @param bool $useAuthorizationHeader Optional. Use Authorization header instead of access_token query parameter.
* @throws MatrixException
*/
public function __construct(string $baseUrl, ?string $token = null, ?string $identity = null,
int $default429WaitMs = 5000, bool $useAuthorizationHeader = true) {
if (!filter_var($baseUrl, FILTER_VALIDATE_URL)) {
throw new MatrixException("Invalid homeserver url $baseUrl");
}
if (!array_get(parse_url($baseUrl), 'scheme')) {
throw new MatrixException("No scheme in homeserver url $baseUrl");
}
$this->baseUrl = $baseUrl;
$this->token = $token;
$this->identity = $identity;
$this->txnId = 0;
$this->validateCert = true;
2018-11-28 09:57:53 +01:00
$this->client = new Client();
$this->default429WaitMs = $default429WaitMs;
$this->useAuthorizationHeader = $useAuthorizationHeader;
2018-11-28 13:22:55 +01:00
}
2018-11-28 09:57:53 +01:00
2018-11-28 13:22:55 +01:00
public function setClient(Client $client) {
$this->client = $client;
2018-11-28 09:57:53 +01:00
}
/**
* @param string|null $since Optional. A token which specifies where to continue a sync from.
* @param int $timeoutMs
* @param null $filter
* @param bool $fullState
* @param string|null $setPresence
* @return array|string
* @throws MatrixException
*/
public function sync(?string $since = null, int $timeoutMs = 30000, $filter = null,
bool $fullState = false, ?string $setPresence = null) {
$request = [
'timeout' => (int)$timeoutMs,
];
if ($since) {
$request['since'] = $since;
}
if ($filter) {
$request['filter'] = $filter;
}
if ($fullState) {
$request['full_state'] = json_encode($fullState);
}
if ($setPresence) {
$request['set_presence'] = $setPresence;
}
return $this->send('GET', "/sync", null, $request);
}
public function validateCertificate(bool $validity) {
$this->validateCert = $validity;
2018-11-28 09:57:53 +01:00
}
/**
* Performs /register.
*
* @param array $authBody Authentication Params.
* @param string $kind Specify kind of account to register. Can be 'guest' or 'user'.
* @param bool $bindEmail Whether to use email in registration and authentication.
* @param string|null $username The localpart of a Matrix ID.
* @param string|null $password The desired password of the account.
* @param string|null $deviceId ID of the client device.
* @param string|null $initialDeviceDisplayName Display name to be assigned.
* @param bool $inhibitLogin Whether to login after registration. Defaults to false.
2018-11-30 00:23:48 +01:00
* @return array|string
* @throws MatrixException
2018-11-28 09:57:53 +01:00
*/
public function register(array $authBody = [], string $kind = "user", bool $bindEmail = false,
?string $username = null, ?string $password = null, ?string $deviceId = null,
?string $initialDeviceDisplayName = null, bool $inhibitLogin = false) {
$content = [
'kind' => $kind
];
2018-11-30 00:23:48 +01:00
if ($authBody) {
$content['auth'] = $authBody;
}
if ($username) {
$content['username'] = $username;
}
if ($password) {
$content['password'] = $password;
}
if ($deviceId) {
$content['device_id'] = $deviceId;
}
if ($initialDeviceDisplayName) {
$content['initial_device_display_name'] = $initialDeviceDisplayName;
}
if ($bindEmail) {
$content['bind_email'] = $bindEmail;
}
if ($inhibitLogin) {
$content['inhibit_login'] = $inhibitLogin;
}
return $this->send('POST', '/register', $content, ['kind' => $kind]);
}
/**
* Perform /login.
*
* @param string $loginType The value for the 'type' key.
* @param array $args Additional key/values to add to the JSON submitted.
* @return array|string
* @throws MatrixException
*/
public function login(string $loginType, array $args) {
$args["type"] = $loginType;
return $this->send('POST', '/login', $args);
}
/**
* Perform /logout.
*
* @return array|string
* @throws MatrixException
*/
public function logout() {
return $this->send('POST', '/logout');
}
/**
* Perform /logout/all.
*
* @return array|string
* @throws MatrixException
*/
public function logoutAll() {
return $this->send('POST', '/logout/all');
}
/**
* Perform /createRoom.
*
* @param string|null $alias Optional. The room alias name to set for this room.
* @param string|null $name Optional. Name for new room.
* @param bool $isPublic Optional. The public/private visibility.
* @param array|null $invitees Optional. The list of user IDs to invite.
* @param bool|null $federate Optional. Сan a room be federated. Default to True.
* @return array|string
* @throws MatrixException
*/
public function createRoom(string $alias = null, string $name = null, bool $isPublic = false,
array $invitees = null, bool $federate = null) {
$content = [
"visibility" => $isPublic ? "public" : "private"
];
if ($alias) {
$content["room_alias_name"] = $alias;
}
if ($invitees) {
$content["invite"] = $invitees;
}
if ($name) {
$content["name"] = $name;
}
if ($federate != null) {
$content["creation_content"] = ['m.federate' => $federate];
}
return $this->send("POST", "/createRoom", $content);
}
/**
* Performs /join/$room_id
*
* @param string $roomIdOrAlias The room ID or room alias to join.
* @return array|string
* @throws MatrixException
*/
public function joinRoom(string $roomIdOrAlias) {
$path = sprintf("/join/%s", urlencode($roomIdOrAlias));
return $this->send('POST', $path);
}
/**
* Perform PUT /rooms/$room_id/state/$event_type
*
* @param string $roomId The room ID to send the state event in.
* @param string $eventType The state event type to send.
* @param array $content The JSON content to send.
* @param string $stateKey Optional. The state key for the event.
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
*/
public function sendStateEvent(string $roomId, string $eventType, array $content,
string $stateKey = "", int $timestamp = null) {
$path = sprintf("/rooms/%s/state/%s", urlencode($roomId), urlencode($eventType));
if ($stateKey) {
$path .= sprintf("/%s", urlencode($stateKey));
}
$params = [];
if ($timestamp) {
$params["ts"] = $timestamp;
}
return $this->send('PUT', $path, $content, $params);
}
/**
* Perform GET /rooms/$room_id/state/$event_type
*
* @param string $roomId The room ID.
* @param string $eventType The type of the event.
* @return array|string
* @throws MatrixRequestException (code=404) if the state event is not found.
* @throws MatrixException
*/
public function getStateEvent(string $roomId, string $eventType) {
$path = sprintf('/rooms/%s/state/%s', urlencode($roomId), urlencode($eventType));
return $this->send('GET', $path);
}
/**
* @param string $roomId The room ID to send the message event in.
* @param string $eventType The event type to send.
* @param array $content The JSON content to send.
* @param int $txnId Optional. The transaction ID to use.
* @param int $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendMessageEvent(string $roomId, string $eventType, array $content,
int $txnId = null, int $timestamp = null) {
if (!$txnId) {
$txnId = $this->makeTxnId();
}
$path = sprintf('/rooms/%s/send/%s/%s', urlencode($roomId), urlencode($eventType), urlencode($txnId));
$params = [];
if ($timestamp) {
$params['ts'] = $timestamp;
}
return $this->send('PUT', $path, $content, $params);
}
/**
* Perform PUT /rooms/$room_id/redact/$event_id/$txn_id/
*
* @param string $roomId The room ID to redact the message event in.
* @param string $eventId The event id to redact.
* @param string $reason Optional. The reason the message was redacted.
* @param int|null $txnId Optional. The transaction ID to use.
* @param int|null $timestamp Optional. Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function redactEvent(string $roomId, string $eventId, ?string $reason = null,
int $txnId = null, int $timestamp = null) {
if (!$txnId) {
$txnId = $this->makeTxnId();
}
$path = sprintf('/rooms/%s/redact/%s/%s', urlencode($roomId), urlencode($eventId), urlencode($txnId));
$params = [];
$content = [];
if ($reason) {
$content['reason'] = $reason;
}
if ($timestamp) {
$params['ts'] = $timestamp;
}
return $this->send('PUT', $path, $content, $params);
}
/**
* $content_type can be a image,audio or video
* extra information should be supplied, see
* https://matrix.org/docs/spec/r0.0.1/client_server.html
*
* @param string $roomId
* @param string $itemUrl
* @param string $itemName
* @param string $msgType
* @param array $extraInformation
* @param int|null $timestamp
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendContent(string $roomId, string $itemUrl, string $itemName, string $msgType,
array $extraInformation = [], int $timestamp = null) {
$contentPack = [
"url" => $itemUrl,
"msgtype" => $msgType,
"body" => $itemName,
"info" => $extraInformation,
];
return $this->sendMessageEvent($roomId, 'm.room.message', $contentPack, null, $timestamp);
}
/**
* Send m.location message event
* http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location
*
* @param string $roomId The room ID to send the event in.
* @param string $geoUri The geo uri representing the location.
* @param string $name Description for the location.
* @param string|null $thumbUrl URL to the thumbnail of the location.
* @param array|null $thumbInfo Metadata about the thumbnail, type ImageInfo.
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendLocation(string $roomId, string $geoUri, string $name, string $thumbUrl = null,
array $thumbInfo = null, int $timestamp = null) {
$contentPack = [
"geo_uri" => $geoUri,
"msgtype" => "m.location",
"body" => $name,
];
if ($thumbUrl) {
$contentPack['thumbnail_url'] = $thumbUrl;
}
if ($thumbInfo) {
$contentPack['thumbnail_info'] = $thumbInfo;
}
return $this->sendMessageEvent($roomId, 'm.room.message', $contentPack, null, $timestamp);
}
/**
* Perform PUT /rooms/$room_id/send/m.room.message
*
* @param string $roomId The room ID to send the event in.
* @param string $textContent The m.text body to send.
* @param string $msgType
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendMessage(string $roomId, string $textContent, string $msgType = 'm.text', int $timestamp = null) {
$textBody = $this->getTextBody($textContent, $msgType);
return $this->sendMessageEvent($roomId, 'm.room.message', $textBody, null, $timestamp);
}
/**
* Perform PUT /rooms/$room_id/send/m.room.message with m.emote msgtype
*
* @param string $roomId The room ID to send the event in.
* @param string $textContent The m.emote body to send.
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendEmote(string $roomId, string $textContent, int $timestamp = null) {
$body = $this->getEmoteBody($textContent);
return $this->sendMessageEvent($roomId, 'm.room.message', $body, null, $timestamp);
}
/**
* Perform PUT /rooms/$room_id/send/m.room.message with m.notice msgtype
*
* @param string $roomId The room ID to send the event in.
* @param string $textContent The m.emote body to send.
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendNotice(string $roomId, string $textContent, int $timestamp = null) {
$body = [
'msgtype' => 'm.notice',
'body' => $textContent,
];
return $this->sendMessageEvent($roomId, 'm.room.message', $body, null, $timestamp);
}
/**
* @param string $roomId The room's id.
* @param string $token The token to start returning events from.
* @param string $direction The direction to return events from. One of: ["b", "f"].
* @param int $limit The maximum number of events to return.
* @param string|null $to The token to stop returning events at.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getRoomMessages(string $roomId, string $token, string $direction, int $limit = 10, string $to = null) {
$query = [
"roomId" => $roomId,
"from" => $token,
'dir' => $direction,
'limit' => $limit,
];
if ($to) {
$query['to'] = $to;
}
$path = sprintf('/rooms/%s/messages', urlencode($roomId));
return $this->send('GET', $path, null, $query);
}
/**
* Perform GET /rooms/$room_id/state/m.room.name
*
* @param string $roomId The room ID
* @return array|string
* @throws MatrixException
* @throws MatrixRequestException
*/
public function getRoomName(string $roomId) {
return $this->getStateEvent($roomId, 'm.room.name');
}
/**
* Perform PUT /rooms/$room_id/state/m.room.name
*
* @param string $roomId The room ID
* @param string $name The new room name
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
*/
public function setRoomName(string $roomId, string $name, int $timestamp = null) {
$body = ['name' => $name];
return $this->sendStateEvent($roomId, 'm.room.name', $body, '', $timestamp);
}
/**
* Perform GET /rooms/$room_id/state/m.room.topic
*
* @param string $roomId The room ID
* @return array|string
* @throws MatrixException
* @throws MatrixRequestException
*/
public function getRoomTopic(string $roomId) {
return $this->getStateEvent($roomId, 'm.room.topic');
}
/**
* Perform PUT /rooms/$room_id/state/m.room.topic
*
* @param string $roomId The room ID
* @param string $topic The new room topic
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
*/
public function setRoomTopic(string $roomId, string $topic, int $timestamp = null) {
$body = ['topic' => $topic];
return $this->sendStateEvent($roomId, 'm.room.topic', $body, '', $timestamp);
}
/**
* Perform GET /rooms/$room_id/state/m.room.power_levels
*
*
* @param string $roomId The room ID
* @return array|string
* @throws MatrixException
* @throws MatrixRequestException
*/
public function getPowerLevels(string $roomId) {
return $this->getStateEvent($roomId, 'm.room.power_levels');
}
/**
* Perform PUT /rooms/$room_id/state/m.room.power_levels
*
* Note that any power levels which are not explicitly specified
* in the content arg are reset to default values.
*
*
* Example:
* $api = new MatrixHttpApi("http://example.com", $token="foobar")
* $api->setPowerLevels("!exampleroom:example.com",
* [
* "ban" => 50, # defaults to 50 if unspecified
* "events": [
* "m.room.name" => 100, # must have PL 100 to change room name
* "m.room.power_levels" => 100 # must have PL 100 to change PLs
* ],
* "events_default" => 0, # defaults to 0
* "invite" => 50, # defaults to 50
* "kick" => 50, # defaults to 50
* "redact" => 50, # defaults to 50
* "state_default" => 50, # defaults to 50 if m.room.power_levels exists
* "users" => [
* "@someguy:example.com" => 100 # defaults to 0
* ],
* "users_default" => 0 # defaults to 0
* ]
* );
*
* @param string $roomId
* @param array $content
* @return array|string
* @throws MatrixException
*/
public function setPowerLevels(string $roomId, array $content) {
// Synapse returns M_UNKNOWN if body['events'] is omitted,
// as of 2016-10-31
if (!array_key_exists('events', $content)) {
$content['events'] = [];
}
return $this->sendStateEvent($roomId, 'm.room.power_levels', $content);
}
/**
* Perform POST /rooms/$room_id/leave
*
* @param string $roomId The room ID
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function leaveRoom(string $roomId) {
return $this->send('POST', sprintf('/rooms/%s/leave', urlencode($roomId)), []);
}
/**
* Perform POST /rooms/$room_id/forget
*
* @param string $roomId The room ID
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function forgetRoom(string $roomId) {
return $this->send('POST', sprintf('/rooms/%s/forget', urlencode($roomId)), []);
}
/**
* Perform POST /rooms/$room_id/invite
*
* @param string $roomId The room ID
* @param string $userId The user ID of the invitee
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function inviteUser(string $roomId, string $userId) {
$body = ['user_id' => $userId];
2021-05-31 06:06:33 +02:00
return $this->send('POST', sprintf('/rooms/%s/invite', urlencode($roomId)), $body);
2018-11-30 00:23:48 +01:00
}
/**
* Calls set_membership with membership="leave" for the user_id provided
*
* @param string $roomId The room ID
* @param string $userId The user ID
* @param string $reason Optional. The reason for kicking them out
* @return mixed
* @throws MatrixException
*/
public function kickUser(string $roomId, string $userId, string $reason = '') {
return $this->setMembership($roomId, $userId, 'leave', $reason);
}
/**
* Perform GET /rooms/$room_id/state/m.room.member/$user_id
*
* @param string $roomId The room ID
* @param string $userId The user ID
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getMembership(string $roomId, string $userId) {
$path = sprintf('/rooms/%s/state/m.room.member/%s', urlencode($roomId), urlencode($userId));
return $this->send('GET', $path);
}
/**
* Perform PUT /rooms/$room_id/state/m.room.member/$user_id
*
* @param string $roomId The room ID
* @param string $userId The user ID
* @param string $membership New membership value
* @param string $reason The reason
* @param array $profile
* @param int|null $timestamp Set origin_server_ts (For application services only)
* @return array|string
* @throws MatrixException
*/
public function setMembership(string $roomId, string $userId, string $membership, string $reason = '', array $profile = [], int $timestamp = null) {
$body = [
'membership' => $membership,
'reason' => $reason,
];
if (array_key_exists('displayname', $profile)) {
$body['displayname'] = $profile['displayname'];
}
if (array_key_exists('avatar_url', $profile)) {
$body['avatar_url'] = $profile['avatar_url'];
}
return $this->sendStateEvent($roomId, 'm.room.member', $body, $userId, $timestamp);
}
/**
* Perform POST /rooms/$room_id/ban
*
* @param string $roomId The room ID
* @param string $userId The user ID of the banee(sic)
* @param string $reason The reason for this ban
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function banUser(string $roomId, string $userId, string $reason = '') {
$body = [
'user_id' => $userId,
'reason' => $reason,
];
return $this->send('POST', sprintf('/rooms/%s/ban', urlencode($roomId)), $body);
}
/**
* Perform POST /rooms/$room_id/unban
*
* @param string $roomId The room ID
* @param string $userId The user ID of the banee(sic)
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function unbanUser(string $roomId, string $userId) {
$body = [
'user_id' => $userId,
];
return $this->send('POST', sprintf('/rooms/%s/unban', urlencode($roomId)), $body);
}
/**
* @param string $userId
* @param string $roomId
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getUserTags(string $userId, string $roomId) {
$path = sprintf('/user/%s/rooms/%s/tags', urlencode($userId), urlencode($roomId));
return $this->send('GET', $path);
}
/**
* @param string $userId
* @param string $roomId
* @param string $tag
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function removeUserTag(string $userId, string $roomId, string $tag) {
$path = sprintf('/user/%s/rooms/%s/tags/%s', urlencode($userId), urlencode($roomId), urlencode($tag));
return $this->send('DELETE', $path);
}
/**
* @param string $userId
* @param string $roomId
* @param string $tag
* @param float|null $order
* @param array $body
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
2018-12-02 11:57:02 +01:00
public function addUserTag(string $userId, string $roomId, string $tag, ?float $order = null, array $body = []) {
2018-11-30 00:23:48 +01:00
if ($order) {
$body['order'] = $order;
}
$path = sprintf('/user/%s/rooms/%s/tags/%s', urlencode($userId), urlencode($roomId), urlencode($tag));
return $this->send('PUT', $path, $body);
}
/**
* @param string $userId
* @param string $type
* @param array $accountData
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function setAccountData(string $userId, string $type, array $accountData) {
$path = sprintf("/user/%s/account_data/%s", urlencode($userId), urlencode($type));
return $this->send('PUT', $path, $accountData);
}
/**
* @param string $userId
* @param string $roomId
* @param string $type
* @param array $accountData
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function setRoomAccountData(string $userId, string $roomId, string $type, array $accountData) {
$path = sprintf(
'/user/%s/rooms/%s/account_data/%s',
urlencode($userId), urlencode($roomId), urlencode($type)
);
return $this->send('PUT', $path, $accountData);
}
/**
* Perform GET /rooms/$room_id/state
*
* @param string $roomId The room ID
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getRoomState(string $roomId) {
return $this->send('GET', sprintf('/rooms/%s/state', urlencode($roomId)));
}
public function getTextBody(string $textContent, string $msgType = 'm.text'): array {
return [
'msgtype' => $msgType,
'body' => $textContent,
];
}
2018-11-28 09:57:53 +01:00
2018-11-30 00:23:48 +01:00
private function getEmoteBody(string $textContent): array {
return $this->getTextBody($textContent, 'm.emote');
2018-11-28 09:57:53 +01:00
}
2018-11-30 00:23:48 +01:00
/**
* @param string $userId
* @param string $filterId
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getFilter(string $userId, string $filterId) {
$path = sprintf("/user/%s/filter/%s", urlencode($userId), urlencode($filterId));
return $this->send('GET', $path);
}
/**
* @param string $userId
* @param array $filterParams
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function createFilter(string $userId, array $filterParams) {
$path = sprintf("/user/%s/filter", urlencode($userId));
return $this->send('POST', $path, $filterParams);
2018-11-28 09:57:53 +01:00
}
/**
* @param string $method
* @param string $path
* @param mixed $content
* @param array $queryParams
* @param array $headers
* @param string $apiPath
* @param bool $returnJson
* @return array|string
* @throws MatrixException
2018-11-30 00:23:48 +01:00
* @throws MatrixRequestException
* @throws MatrixHttpLibException
2018-11-28 09:57:53 +01:00
*/
private function send(string $method, string $path, $content = null, array $queryParams = [], array $headers = [],
$apiPath = self::MATRIX_V2_API_PATH, $returnJson = true) {
$options = [];
if (!in_array('User-Agent', $headers)) {
2018-11-30 00:23:48 +01:00
$headers['User-Agent'] = 'php-matrix-sdk/' . self::VERSION;
2018-11-28 09:57:53 +01:00
}
$method = strtoupper($method);
if (!in_array($method, ['GET', 'POST', 'PUT', 'DELETE'])) {
throw new MatrixException("Unsupported HTTP method: $method");
}
if (!in_array('Content-Type', $headers)) {
$headers['Content-Type'] = 'application/json';
}
if ($this->useAuthorizationHeader) {
$headers['Authorization'] = sprintf('Bearer %s', $this->token);
} else {
$queryParams['access_token'] = $this->token;
}
if ($this->identity) {
$queryParams['user_id'] = $this->identity;
}
$options = array_merge($options, [
'headers' => $headers,
'query' => $queryParams,
'verify' => $this->validateCert,
2018-11-28 09:57:53 +01:00
]);
$endpoint = $this->baseUrl . $apiPath . $path;
if ($headers['Content-Type'] == "application/json" && $content !== null) {
$options[RequestOptions::JSON] = $content;
}
else {
$options[RequestOptions::FORM_PARAMS] = $content;
}
2018-11-28 09:57:53 +01:00
$responseBody = '';
while (true) {
try {
$response = $this->client->request($method, $endpoint, $options);
2018-11-28 13:25:06 +01:00
} catch (GuzzleException $e) {
2018-11-28 09:57:53 +01:00
throw new MatrixHttpLibException($e, $method, $endpoint);
}
if ($response->getStatusCode() != 429) {
$responseBody = $response->getBody()->getContents();
break;
}
$jsonResponse = json_decode($responseBody, true);
$waitTime = array_get($jsonResponse, 'retry_after_ms');
$waitTime = $waitTime ?: array_get($jsonResponse, 'error.retry_after_ms', $this->default429WaitMs);
$waitTime /= 1000;
sleep($waitTime);
}
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw new MatrixRequestException($response->getStatusCode(), $responseBody);
}
return $returnJson ? json_decode($responseBody, true) : $responseBody;
}
2018-11-30 00:23:48 +01:00
/**
* @param $content
* @param string $contentType
* @param string|null $filename
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function mediaUpload($content, string $contentType, string $filename = null) {
$headers = ['Content-Type' => $contentType];
$apiPath = self::MATRIX_V2_MEDIA_PATH . "/upload";
$queryParam = [];
if ($filename) {
$queryParam['filename'] = $filename;
}
return $this->send('POST', '', $content, $queryParam, $headers, $apiPath);
}
/**
* @param string $userId
* @return string|null
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getDisplayName(string $userId): ?string {
$content = $this->send("GET", "/profile/$userId/displayname");
return array_get($content, 'displayname');
}
/**
* @param string $userId
* @param string $displayName
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function setDisplayName(string $userId, string $displayName) {
$content = ['displayname' => $displayName];
$path = sprintf('/profile/%s/displayname', urlencode($userId));
return $this->send('PUT', $path, $content);
}
/**
* @param string $userId
* @return mixed
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getAvatarUrl(string $userId): ?string {
$content = $this->send("GET", "/profile/$userId/avatar_url");
return array_get($content, 'avatar_url');
}
/**
* @param string $userId
* @param string $avatarUrl
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function setAvatarUrl(string $userId, string $avatarUrl) {
$content = ['avatar_url' => $avatarUrl];
$path = sprintf('/profile/%s/avatar_url', urlencode($userId));
return $this->send('PUT', $path, $content);
}
/**
* @param string $mxcurl
* @return string
* @throws ValidationException
*/
public function getDownloadUrl(string $mxcurl): string {
Util::checkMxcUrl($mxcurl);
return $this->baseUrl . self::MATRIX_V2_MEDIA_PATH . "/download/" . substr($mxcurl, 6);
}
/**
* Download raw media from provided mxc URL.
*
* @param string $mxcurl mxc media URL.
* @param bool $allowRemote Indicates to the server that it should not
* attempt to fetch the media if it is deemed remote. Defaults
* to true if not provided.
* @return string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
* @throws ValidationException
*/
public function mediaDownload(string $mxcurl, bool $allowRemote = true) {
Util::checkMxcUrl($mxcurl);
$queryParam = [];
if (!$allowRemote) {
$queryParam["allow_remote"] = false;
}
$path = substr($mxcurl, 6);
$apiPath = self::MATRIX_V2_MEDIA_PATH . "/download/";
return $this->send('GET', $path, null, $queryParam, [], $apiPath, false);
}
/**
* Download raw media thumbnail from provided mxc URL.
*
* @param string $mxcurl mxc media URL
* @param int $width desired thumbnail width
* @param int $height desired thumbnail height
* @param string $method thumb creation method. Must be
* in ['scale', 'crop']. Default 'scale'.
* @param bool $allowRemote indicates to the server that it should not
* attempt to fetch the media if it is deemed remote. Defaults
* to true if not provided.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
* @throws ValidationException
*/
public function getThumbnail(string $mxcurl, int $width, int $height,
string $method = 'scale', bool $allowRemote = true) {
Util::checkMxcUrl($mxcurl);
if (!in_array($method, ['scale', 'crop'])) {
throw new ValidationException('Unsupported thumb method ' . $method);
}
$queryParams = [
"width" => $width,
"height" => $height,
"method" => $method,
];
if (!$allowRemote) {
$queryParams["allow_remote"] = false;
}
$path = substr($mxcurl, 6);
$apiPath = self::MATRIX_V2_MEDIA_PATH . "/thumbnail/";
return $this->send('GET', $path, null, $queryParams, [], $apiPath, false);
}
/**
* Get preview for URL.
*
* @param string $url URL to get a preview
* @param float|null $ts The preferred point in time to return
* a preview for. The server may return a newer
* version if it does not have the requested
* version available.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getUrlPreview(string $url, float $ts = null) {
$params = ['url' => $url];
if ($ts) {
$params['ts'] = $ts;
}
$apiPath = self::MATRIX_V2_MEDIA_PATH . '/preview_url';
return $this->send('GET', '', null, $params, [], $apiPath);
}
/**
* Get room id from its alias.
*
* @param string $roomAlias The room alias name.
* @return null|string Wanted room's id.
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getRoomId(string $roomAlias): ?string {
$content = $this->send('GET', sprintf("/directory/room/%s", urlencode($roomAlias)));
return array_get($content, 'room_id');
}
/**
* Set alias to room id
*
* @param string $roomId The room id.
* @param string $roomAlias The room wanted alias name.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function setRoomAlias(string $roomId, string $roomAlias) {
$content = ['room_id' => $roomId];
return $this->send('PUT', sprintf("/directory/room/%s", urlencode($roomAlias)), $content);
}
/**
* Remove mapping of an alias
*
* @param string $roomAlias The alias to be removed.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function removeRoomAlias(string $roomAlias) {
return $this->send('DELETE', sprintf("/directory/room/%s", urlencode($roomAlias)));
}
/**
* Get the list of members for this room.
*
* @param string $roomId The room to get the member events for.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getRoomMembers(string $roomId) {
return $this->send('GET', sprintf("/rooms/%s/members", urlencode($roomId)));
}
/**
* Set the rule for users wishing to join the room.
*
* @param string $roomId The room to set the rules for.
* @param string $joinRule The chosen rule. One of: ["public", "knock", "invite", "private"]
* @return array|string
* @throws MatrixException
*/
public function setJoinRule(string $roomId, string $joinRule) {
$content = ['join_rule' => $joinRule];
return $this->sendStateEvent($roomId, 'm.room.join_rule', $content);
}
/**
* Set the guest access policy of the room.
*
* @param string $roomId The room to set the rules for.
* @param string $guestAccess Wether guests can join. One of: ["can_join", "forbidden"]
* @return array|string
* @throws MatrixException
*/
public function setGuestAccess(string $roomId, string $guestAccess) {
$content = ['guest_access' => $guestAccess];
return $this->sendStateEvent($roomId, 'm.room.guest_access', $content);
}
/**
* Gets information about all devices for the current user.
*
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getDevices() {
return $this->send('GET', '/devices');
}
/**
* Gets information on a single device, by device id.
*
* @param string $deviceId
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function getDevice(string $deviceId) {
return $this->send('GET', sprintf('/devices/%s', urlencode($deviceId)));
}
/**
* Update the display name of a device.
*
* @param string $deviceId The device ID of the device to update.
* @param string $displayName New display name for the device.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function updateDeviceInfo(string $deviceId, string $displayName) {
$content = ['display_name' => $displayName];
return $this->send('PUT', sprintf('/devices/%s', urlencode($deviceId)), $content);
}
/**
* Deletes the given device, and invalidates any access token associated with it.
*
* NOTE: This endpoint uses the User-Interactive Authentication API.
*
* @param array $authBody Authentication params.
* @param string $deviceId The device ID of the device to delete.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function deleteDevice(array $authBody, string $deviceId) {
$content = ['auth' => $authBody];
return $this->send('DELETE', sprintf('/devices/%s', urlencode($deviceId)), $content);
}
/**
* Bulk deletion of devices.
*
* NOTE: This endpoint uses the User-Interactive Authentication API.
*
* @param array $authBody Authentication params.
* @param array $devices List of device ID"s to delete.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function deleteDevices($authBody, $devices) {
$content = [
'auth' => $authBody,
'devices' => $devices
];
return $this->send('POST', '/delete_devices', $content);
}
/**
* Publishes end-to-end encryption keys for the device.
* Said device must be the one used when logging in.
*
* @param array $deviceKeys Optional. Identity keys for the device. The required keys are:
* | user_id (str): The ID of the user the device belongs to. Must match the user ID used when logging in.
* | device_id (str): The ID of the device these keys belong to. Must match the device ID used when logging in.
* | algorithms (list<str>): The encryption algorithms supported by this device.
* | keys (dict): Public identity keys. Should be formatted as <algorithm:device_id>: <key>.
* | signatures (dict): Signatures for the device key object. Should be formatted as <user_id>: {<algorithm:device_id>: <key>}
* @param array $oneTimeKeys Optional. One-time public keys. Should be
* formatted as <algorithm:key_id>: <key>, the key format being
* determined by the algorithm.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function uploadKeys(array $deviceKeys = [], array $oneTimeKeys = []) {
$content = [];
if ($deviceKeys) {
$content['device_keys'] = $deviceKeys;
}
if ($oneTimeKeys) {
$content['one_time_keys'] = $oneTimeKeys;
}
2018-12-01 10:42:29 +01:00
return $this->send('POST', '/keys/upload', $content ?: null);
2018-11-30 00:23:48 +01:00
}
/**
* Query HS for public keys by user and optionally device.
*
* @param array $userDevices The devices whose keys to download. Should be
* formatted as <user_id>: [<device_ids>]. No device_ids indicates
* all devices for the corresponding user.
* @param int $timeout Optional. The time (in milliseconds) to wait when
* downloading keys from remote servers.
* @param string $token Optional. If the client is fetching keys as a result of
* a device update received in a sync request, this should be the
* 'since' token of that sync request, or any later sync token.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function queryKeys(array $userDevices, int $timeout = null, string $token = null) {
$content = ['device_keys' => $userDevices];
if ($timeout) {
$content['timeout'] = $timeout;
}
if ($token) {
$content['token'] = $token;
}
return $this->send('POST', "/keys/query", $content);
}
/**
* Claims one-time keys for use in pre-key messages.
*
* @param array $keyRequest The keys to be claimed. Format should be <user_id>: { <device_id>: <algorithm> }.
* @param int $timeout Optional. The time (in ms) to wait when downloading keys from remote servers.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function claimKeys(array $keyRequest, int $timeout) {
$content = ['one_time_keys' => $keyRequest];
if ($timeout) {
$content['timeout'] = $timeout;
}
return $this->send('POST', "/keys/claim", $content);
}
/**
* Gets a list of users who have updated their device identity keys.
*
* @param string $fromToken The desired start point of the list. Should be the
* next_batch field from a response to an earlier call to /sync.
* @param string $toToken The desired end point of the list. Should be the next_batch
* field from a recent call to /sync - typically the most recent such call.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function keyChanges(string $fromToken, string $toToken) {
$params = [
'from' => $fromToken,
'to' => $toToken,
];
return $this->send("GET", "/keys/changes", null, $params);
}
/**
* Sends send-to-device events to a set of client devices.
*
* @param string $eventType The type of event to send.
* @param array $messages The messages to send. Format should be
* <user_id>: {<device_id>: <event_content>}.
* The device ID may also be '*', meaning all known devices for the user.
* @param string|null $txnId Optional. The transaction ID for this event, will be generated automatically otherwise.
* @return array|string
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function sendToDevice(string $eventType, array $messages, string $txnId = null) {
$txnId = $txnId ?: $this->makeTxnId();
$content = ['messages' => $messages];
$path = sprintf("/sendToDevice/%s/%s", urlencode($eventType), urlencode($txnId));
return $this->send('PUT', $path, $content);
}
private function makeTxnId(): int {
$txnId = $this->txnId . (int)(microtime(true) * 1000);
$this->txnId++;
return $txnId;
}
/**
* Determine user_id for authenticated user.
*
* @return array
* @throws MatrixException
* @throws MatrixHttpLibException
* @throws MatrixRequestException
*/
public function whoami(): array {
if (!$this->token) {
throw new MatrixException('Authentication required.');
}
return $this->send('GET', '/account/whoami');
}
2018-12-01 01:47:23 +01:00
public function setToken(?string $token) {
$this->token = $token;
}
2018-11-30 00:23:48 +01:00
}