diff --git a/src/MatrixClient.php b/src/MatrixClient.php index a18b94b..f8e0ac3 100644 --- a/src/MatrixClient.php +++ b/src/MatrixClient.php @@ -93,7 +93,7 @@ class MatrixClient { * A `User` object in this array is shared between all `Room` * objects where the corresponding user is joined. */ - protected $users = []; + public $users = []; protected $userId; protected $token; protected $hs; @@ -423,7 +423,7 @@ class MatrixClient { try { $event = $this->api->getStateEvent($roomId, "m.room.encryption"); if ($event['algorithm'] === "m.megolm.v1.aes-sha2") { - $room->setEncrytion(true); + $room->enableEncryption(); } } catch (MatrixRequestException $e) { if ($e->getHttpCode() != 404) { @@ -528,4 +528,16 @@ class MatrixClient { return true; } + public function api(): MatrixHttpApi { + return $this->api; + } + + public function userId():?string { + return $this->userId; + } + + public function cacheLevel() { + return $this->cacheLevel; + } + } \ No newline at end of file diff --git a/src/MatrixHttpApi.php b/src/MatrixHttpApi.php index 34b7d54..d782164 100644 --- a/src/MatrixHttpApi.php +++ b/src/MatrixHttpApi.php @@ -789,7 +789,7 @@ class MatrixHttpApi { * @throws MatrixHttpLibException * @throws MatrixRequestException */ - public function addUserTag(string $userId, string $roomId, string $tag, ?float $order, array $body = []) { + public function addUserTag(string $userId, string $roomId, string $tag, ?float $order = null, array $body = []) { if ($order) { $body['order'] = $order; } diff --git a/src/Room.php b/src/Room.php index c41bf8a..409abb9 100644 --- a/src/Room.php +++ b/src/Room.php @@ -2,15 +2,894 @@ namespace Aryess\PhpMatrixSdk; +use Aryess\PhpMatrixSdk\Exceptions\MatrixRequestException; +use function GuzzleHttp\default_ca_bundle; +use http\Exception; +use phpDocumentor\Reflection\DocBlock\Tags\Param; + +/** + * Call room-specific functions after joining a room from the client. + * + * NOTE: This should ideally be called from within the Client. + * NOTE: This does not verify the room with the Home Server. + * + * @package Aryess\PhpMatrixSdk + */ class Room { + /** @var MatrixClient */ + protected $client; + protected $roomId; + protected $listeners = []; + protected $stateListeners = []; + protected $ephemeralListeners = []; + protected $events = []; + protected $eventHistoryLimit = 20; + protected $name; + protected $canonicalAlias; + protected $aliases = []; + protected $topic; + protected $inviteOnly = false; + protected $guestAccess; + public $prevBatch; + protected $_members = []; + protected $membersDisplaynames = [ + // $userId: $displayname, + ]; + protected $encrypted = false; + public function __construct(MatrixClient $client, string $roomId) { + Util::checkRoomId($roomId); + $this->roomId = $roomId; + $this->client = $client; + } + + /** + * Set user profile within a room. + * + * This sets displayname and avatar_url for the logged in user only in a + * specific room. It does not change the user's global user profile. + * + * @param string|null $displayname + * @param string|null $avatarUrl + * @param string $reason + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function setUserProfile(?string $displayname = null, ?string $avatarUrl = null, + string $reason = "Changing room profile information") { + $member = $this->api()->getMembership($this->roomId, $this->client->userId()); + if ($member['membership'] != 'join') { + throw new \Exception("Can't set profile if you have not joined the room."); + } + if (!$displayname) { + $displayname = $member["displayname"]; + } + if (!$avatarUrl) { + $avatarUrl = $member["avatar_url"]; + } + $this->api()->setMembership( + $this->roomId, + $this->client->userId(), + 'join', + $reason, + [ + "displayname" => $displayname, + "avatar_url" => $avatarUrl + ] + ); + } + + /** + * Calculates the display name for a room. + * + * @return string + */ + public function displayName() { + if ($this->name) { + return $this->name; + } elseif ($this->canonicalAlias) { + return $this->canonicalAlias; + } + + // Member display names without me + $members = array_reduce($this->getJoinedMembers(), function (array $all, User $u) { + if ($this->client->userId() != $u->userId()) { + $all[] = $u->getDisplayName($this); + } + return $all; + }, []); + sort($members); + + switch (count($members)) { + case 0: + return 'Empty room'; + case 1: + return $members[0]; + case 2: + return sprintf("%s and %s", $members[0], $members[1]); + default: + return sprintf("%s and %d others.", $members[0], count($members)); + } + } + + /** + * Send a plain text message to the room. + * + * @param string $text + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendText(string $text) { + return $this->api()->sendMessageEvent($this->roomId, $text); + } + + public function getHtmlContent(string $html, ?string $body = null, string $msgType = 'm.text') { + return [ + 'body' => $body ?: strip_tags($html), + 'msgtype' => $msgType, + 'format' => "org.matrix.custom.html", + 'formatted_body' => $html, + ]; + } + + /** + * Send an html formatted message. + * + * @param string $html The html formatted message to be sent. + * @param string|null $body The unformatted body of the message to be sent. + * @param string $msgType + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendHtml(string $html, ?string $body = null, string $msgType = 'm.text') { + $content = $this->getHtmlContent($html, $body, $msgType); + + return $this->api()->sendMessageEvent($this->roomId, 'm.room.message', $content); + } + + /** + * @param string $type + * @param array $data + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function setAccountData(string $type, array $data) { + return $this->api()->setRoomAccountData($this->client->userId(), $this->roomId, $type, $data); + } + + /** + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function getTags() { + return $this->api()->getUserTags($this->client->userId(), $this->roomId); + } + + /** + * @param string $tag + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function removeTag(string $tag) { + return $this->api()->removeUserTag($this->client->userId(), $this->roomId, $tag); + } + + /** + * @param string $tag + * @param float|null $order + * @param array $content + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function addTag(string $tag, ?float $order = null, array $content = []) { + return $this->api()->addUserTag($this->client->userId(), $this->roomId, $tag, $order, $content); + } + + /** + * Send an emote (/me style) message to the room. + * + * @param string $text + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendEmote(string $text) { + return $this->api()->sendEmote($this->roomId, $text); + } + + /** + * Send a pre-uploaded file to the room. + * + * See http://matrix.org/docs/spec/r0.4.0/client_server.html#m-file for fileinfo. + * + * @param string $url The mxc url of the file. + * @param string $name The filename of the image. + * @param array $fileinfo Extra information about the file + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendFile(string $url, string $name, array $fileinfo) { + return $this->api()->sendContent($this->roomId, $url, $name, 'm.file', $fileinfo); + } + + /** + * Send a notice (from bot) message to the room. + * + * @param string $text + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendNotice(string $text) { + return $this->api()->sendNotice($this->roomId, $text); + } + + /** + * Send a pre-uploaded image to the room. + * + * See http://matrix.org/docs/spec/r0.0.1/client_server.html#m-image for imageinfo + * + * @param string $url The mxc url of the image. + * @param string $name The filename of the image. + * @param array $fileinfo Extra information about the image. + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendImage(string $url, string $name, ?array $fileinfo) { + return $this->api()->sendContent($this->roomId, $url, $name, 'm.image', $fileinfo); + } + + /** + * Send a location to the room. + * See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location for thumb_info + * + * @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 $thumbInfo Metadata about the thumbnail, type ImageInfo. + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendLocation(string $geoUri, string $name, ?string $thumbUrl = null, ?array $thumbInfo) { + return $this->api()->sendLocation($this->roomId, $geoUri, $name, $thumbUrl, $thumbInfo); + } + + /** + * Send a pre-uploaded video to the room. + * See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video for videoinfo + * + * @param string $url The mxc url of the video. + * @param string $name The filename of the video. + * @param array $videoinfo Extra information about the video. + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendVideo(string $url, string $name, ?array $videoinfo) { + return $this->api()->sendContent($this->roomId, $url, $name, 'm.video', $videoinfo); + } + + /** + * Send a pre-uploaded audio to the room. + * See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio for audioinfo + * + * @param string $url The mxc url of the video. + * @param string $name The filename of the video. + * @param array $audioinfo Extra information about the video. + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function sendAudio(string $url, string $name, ?array $audioinfo) { + return $this->api()->sendContent($this->roomId, $url, $name, 'm.audio', $audioinfo); + } + + /** + * Redacts the message with specified event_id for the given reason. + * + * See https://matrix.org/docs/spec/r0.0.1/client_server.html#id112 + * + * @param string $eventId + * @param string|null $reason + * @return array|string + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws Exceptions\MatrixRequestException + */ + public function redactMessage(string $eventId, ?string $reason = null) { + return $this->api()->redactEvent($this->roomId, $eventId, $reason); + } + + /** + * Add a callback handler for events going to this room. + * + * @param callable $cb (func(room, event)): Callback called when an event arrives. + * @param string|null $eventType The event_type to filter for. + * @return string Unique id of the listener, can be used to identify the listener. + */ + public function addListener(callable $cb, ?string $eventType = null) { + $listenerId = uniqid(); + $this->listeners[] = [ + 'uid' => $listenerId, + 'callback' => $cb, + 'event_type' => $eventType, + ]; + + return $listenerId; + } + + /** + * Remove listener with given uid. + * + * @param string $uid + */ + public function removeListener(string $uid) { + $this->listeners = array_filter($this->listeners, function ($l) use ($uid) { + return $l['uid'] != $uid; + }); + } + + /** + * Add a callback handler for ephemeral events going to this room. + * + * @param callable $cb (func(room, event)): Callback called when an ephemeral event arrives. + * @param string|null $eventType The event_type to filter for. + * @return string Unique id of the listener, can be used to identify the listener. + */ + public function addEphemeralListener(callable $cb, ?string $eventType = null) { + $listenerId = uniqid(); + $this->ephemeralListeners[] = [ + 'uid' => $listenerId, + 'callback' => $cb, + 'event_type' => $eventType, + ]; + + return $listenerId; + } + + /** + * Remove ephemeral listener with given uid. + * + * @param string $uid + */ + public function removeEphemeralListener(string $uid) { + $this->ephemeralListeners = array_filter($this->ephemeralListeners, function ($l) use ($uid) { + return $l['uid'] != $uid; + }); + } + + /** + * Add a callback handler for state events going to this room. + * + * @param callable $cb Callback called when an event arrives. + * @param string|null $eventType The event_type to filter for. + */ + public function addStateListener(callable $cb, ?string $eventType = null) { + $this->stateListeners[] = [ + 'callback' => $cb, + 'event_type' => $eventType, + ]; + } + + protected function putEvent(array $event) { + $this->events[] = $event; + if (count($this->events) > $this->eventHistoryLimit) { + array_pop($this->events); + } + if (array_key_exists('state_event', $event)) { + $this->processStateEvent($event); + } + // Dispatch for room-specific listeners + foreach ($this->listeners as $l) { + if (!$l['event_type'] || $l['event_type'] == $event['event_type']) { + $l['cb']($this, $event); + } + } + } + + protected function putEphemeralEvent(array $event) { + // Dispatch for room-specific listeners + foreach ($this->ephemeralListeners as $l) { + if (!$l['event_type'] || $l['event_type'] == $event['event_type']) { + $l['cb']($this, $event); + } + } + } + + /** + * Get the most recent events for this room. + * + * @return array + */ + public function getEvents(): array { + return $this->events; + } + + /** + * Invite a user to this room. + * + * @param string $userId + * @return bool Whether invitation was sent. + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + */ + public function inviteUser(string $userId): bool { + try { + $this->api()->inviteUser($this->roomId, $userId); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Kick a user from this room. + * + * @param string $userId The matrix user id of a user. + * @param string $reason A reason for kicking the user. + * @return bool Whether user was kicked. + * @throws Exceptions\MatrixException + */ + public function kickUser(string $userId, string $reason = ''): bool { + try { + $this->api()->kickUser($this->roomId, $userId, $reason); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Ban a user from this room. + * + * @param string $userId The matrix user id of a user. + * @param string $reason A reason for banning the user. + * @return bool Whether user was banned. + * @throws Exceptions\MatrixException + */ + public function banUser(string $userId, string $reason = ''): bool { + try { + $this->api()->banUser($this->roomId, $userId, $reason); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Leave the room. + * + * @return bool Leaving the room was successful. + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + */ + public function leave() { + try { + $this->api()->leaveRoom($this->roomId); + $this->client->forgetRoom($this->roomId); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Updates $this->name and returns true if room name has changed. + * @return bool + * @throws Exceptions\MatrixException + */ + public function updateRoomName() { + try { + $response = $this->api()->getRoomName($this->roomId); + $newName = array_get($response, 'name', $this->name); + $this->name = $newName; + if ($this->name != $newName) { + $this->name = $newName; + return true; + } + } catch (MatrixRequestException $e) { + } + + return false; + } + + /** + * Return True if room name successfully changed. + * + * @param string $name + * @return bool + * @throws Exceptions\MatrixException + */ + public function setRoomName(string $name) { + try { + $this->api()->setRoomName($this->roomId, $name); + $this->name = $name; + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Send a state event to the room. + * + * @param string $eventType The type of event that you are sending. + * @param array $content An object with the content of the message. + * @param string $stateKey Optional. A unique key to identify the state. + * @throws Exceptions\MatrixException + */ + public function sendStateEvent(string $eventType, array $content, string $stateKey = '') { + $this->api()->sendStateEvent($this->roomId, $eventType, $content, $stateKey); + } + + /** + * Updates $this->topic and returns true if room topic has changed. + * + * @return bool + * @throws Exceptions\MatrixException + */ + public function updateRoomTopic() { + try { + $response = $this->api()->getRoomTopic($this->roomId); + $oldTopic = $this->topic; + $this->topic = array_get($response, 'topic', $this->topic); + } catch (MatrixRequestException $e) { + return false; + } + + return $oldTopic == $this->topic; + } + + /** + * Return True if room topic successfully changed. + * + * @param string $topic + * @return bool + * @throws Exceptions\MatrixException + */ + public function setRoomTopic(string $topic) { + try { + $this->api()->setRoomTopic($this->roomId, $topic); + $this->topic = $topic; + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Get aliases information from room state. + * + * @return bool True if the aliases changed, False if not + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + */ + public function updateAliases() { + try { + $response = $this->api()->getRoomState($this->roomId); + $oldAliases = $this->aliases; + foreach ($response as $chunk) { + if ($aliases = array_get($chunk, 'content.aliases')) { + $this->aliases = $aliases; + return $this->aliases == $oldAliases; + } + } + } catch (MatrixRequestException $e) { + return false; + } + } + + /** + * Add an alias to the room and return True if successful. + * + * @param string $alias + * @return bool + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + */ + public function addRoomAlias(string $alias) { + try { + $this->api()->setRoomAlias($this->roomId, $alias); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + public function getJoinedMembers() { + if ($this->_members) { + return array_values($this->_members); + } + $response = $this->api()->getRoomMembers($this->roomId); + foreach ($response['chunk'] as $event) { + if (array_get($event, 'event.membership') == 'join') { + $userId = $event['state_key']; + $this->addMember($userId, array_get($event, 'content.displayname')); + } + } + + return array_values($this->_members); + } + + protected function addMember(string $userId, ?string $displayname) { + if ($displayname) { + $this->membersDisplaynames[$userId] = $displayname; + } + if (array_key_exists($userId, $this->_members)) { + return; + } + if (array_key_exists($userId, $this->client->users)) { + $this->_members[$userId] = $this->client->users[$userId]; + return; + } + $this->_members[$userId] = new User($this->api(), $userId, $displayname); + $this->client->users[$userId] = $this->_members[$userId]; + } + + /** + * Backfill handling of previous messages. + * + * @param bool $reverse When false messages will be backfilled in their original + * order (old to new), otherwise the order will be reversed (new to old). + * @param int $limit Number of messages to go back. + * @throws Exceptions\MatrixException + * @throws Exceptions\MatrixHttpLibException + * @throws MatrixRequestException + */ + public function backfillPreviousMessages(bool $reverse = false, int $limit = 10) { + $res = $this->api()->getRoomMessages($this->roomId, $this->prevBatch, 'b', $limit); + $events = $res['chunk']; + if (!$reverse) { + $events = array_reverse($events); + } + foreach ($events as $event) { + $this->putEvent($event); + } + } + + /** + * Modify the power level for a subset of users + * + * @param array $users Power levels to assign to specific users, in the form + * {"@name0:host0": 10, "@name1:host1": 100, "@name3:host3", None} + * A level of None causes the user to revert to the default level + * as specified by users_default. + * @param int $userDefault Default power level for users in the room + * @return bool + * @throws Exceptions\MatrixException + */ + public function modifyUserPowerLevels(array $users = null, int $userDefault = null) { + try { + $content = $this->api()->getPowerLevels($this->roomId); + if ($userDefault) { + $content['user_default'] = $userDefault; + } + + if ($users) { + if (array_key_exists('users', $content)) { + $content['users'] = array_merge($content['users'], $content); + } else { + $content['users'] = $users; + } + + // Remove any keys with value null + foreach ($content['users'] as $user => $pl) { + if (!$pl) { + unset($content['users'][$user]); + } + } + } + + $this->api()->setPowerLevels($this->roomId, $content); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Modifies room power level requirements. + * + * @param array $events Power levels required for sending specific event types, + * in the form {"m.room.whatever0": 60, "m.room.whatever2": None}. + * Overrides events_default and state_default for the specified + * events. A level of None causes the target event to revert to the + * default level as specified by events_default or state_default. + * @param array $extra Key/value pairs specifying the power levels required for + * various actions: + * + * - events_default(int): Default level for sending message events + * - state_default(int): Default level for sending state events + * - invite(int): Inviting a user + * - redact(int): Redacting an event + * - ban(int): Banning a user + * - kick(int): Kicking a user + * @return bool + * @throws Exceptions\MatrixException + */ + public function modifyRequiredPowerLevels(array $events = [], array $extra = []) { + try { + $content = $this->api()->getPowerLevels($this->roomId); + $content = array_merge($content, $extra); + foreach ($content as $k => $v) { + if (!$v) { + unset($content[$k]); + } + } + + if ($events) { + if (array_key_exists('events', $content)) { + $content["events"] = array_merge($content["events"], $events); + } else { + $content["events"] = $events; + } + + // Remove any keys with value null + foreach ($content['event'] as $event => $pl) { + if (!$pl) { + unset($content['event'][$event]); + } + } + } + + $this->api()->setPowerLevels($this->roomId, $content); + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Set how the room can be joined. + * + * @param bool $inviteOnly If True, users will have to be invited to join + * the room. If False, anyone who knows the room link can join. + * @return bool True if successful, False if not + * @throws Exceptions\MatrixException + */ + public function setInviteOnly(bool $inviteOnly) { + $joinRule = $inviteOnly ? 'invite' : 'public'; + try { + $this->api()->setJoinRule($this->roomId, $joinRule); + $this->inviteOnly = $inviteOnly; + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Set whether guests can join the room and return True if successful. + * + * @param bool $allowGuest + * @return bool + * @throws Exceptions\MatrixException + */ + public function setGuestAccess(bool $allowGuest) { + $guestAccess = $allowGuest ? 'can_join' : 'forbidden'; + try { + $this->api()->setGuestAccess($this->roomId, $guestAccess); + $this->guestAccess = $allowGuest; + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + /** + * Enables encryption in the room. + * + * NOTE: Once enabled, encryption cannot be disabled. + * + * @return bool True if successful, False if not + * @throws Exceptions\MatrixException + */ + public function enableEncryption() { + try { + $this->sendStateEvent('m.room.encryption', ['algorithm' => 'm.megolm.v1.aes-sha2']); + $this->encrypted = true; + } catch (MatrixRequestException $e) { + return false; + } + + return true; + } + + protected function processStateEvent(array $stateEvent) { + if (!array_key_exists('type', $stateEvent)) { + return; + } + $etype = $stateEvent['type']; + $econtent = $stateEvent['content']; + $clevel = $this->client->cacheLevel(); + + // Don't keep track of room state if caching turned off + if ($clevel >= Cache::SOME) { + switch ($etype) { + case 'm.room.name': + $this->name = array_get($econtent, 'name'); + break; + case 'm.room.canonical_alias': + $this->canonicalAlias = array_get($econtent, 'alias'); + break; + case 'm.room.topic': + $this->topic = array_get($econtent, 'topic'); + break; + case 'm.room.aliases': + $this->aliases = array_get($econtent, 'aliases'); + break; + case 'm.room.join_rules': + $this->inviteOnly = $econtent["join_rule"] == "invite"; + break; + case 'm.room.guest_access': + $this->guestAccess = $econtent["guest_access"] == "can_join"; + break; + case 'm.room.encryption': + $this->encrypted = array_get($econtent, 'algorithm') ? true : $this->encrypted; + break; + case 'm.room.member': + // tracking room members can be large e.g. #matrix:matrix.org + if ($clevel == Cache::ALL) { + if ($econtent['membership'] == 'join') { + $userId = $stateEvent['state_key']; + $this->addMember($userId, array_get($econtent, 'displayname')); + } elseif (in_array(econtent["membership"], ["leave", "kick", "invite"])) { + unset($this->_members[array_get($stateEvent, 'state_key')]); + } + } + break; + } + } + + foreach ($this->stateListeners as $listener) { + if (!$listener['event_type'] || $listener['event_type'] == $stateEvent['type']) { + $listener['cb']($stateEvent); + } + } } public function getMembersDisplayNames(): array { - return []; + return $this->membersDisplaynames; } - public function setEncrytion(bool $true) { + protected function api(): MatrixHttpApi { + return $this->client->api(); } + + } \ No newline at end of file diff --git a/src/User.php b/src/User.php index 71a0a58..fc22526 100644 --- a/src/User.php +++ b/src/User.php @@ -38,15 +38,15 @@ class User { * @throws Exceptions\MatrixRequestException */ public function getDisplayName(?Room $room = null): string { - if($room) { + if ($room) { return array_get($room->getMembersDisplayNames(), $this->userId, $this->userId); } - if(!$this->displayName) { + if (!$this->displayName) { $this->displayName = $this->api->getDisplayName($this->userId); } - return $this->displayName?:$this->userId; + return $this->displayName ?: $this->userId; } /** @@ -74,7 +74,7 @@ class User { public function getAvatarUrl(): ?string { $mxurl = $this->api->getAvatarUrl($this->userId); $url = null; - if($mxurl) { + if ($mxurl) { $url = $this->api->getDownloadUrl($mxurl); } @@ -94,5 +94,9 @@ class User { return $this->api->setAvatarUrl($this->userId, $avatarUrl); } + public function userId(): string { + return $this->userId; + } + } \ No newline at end of file