1<?php 2 3namespace MatrixPhp; 4 5use MatrixPhp\Exceptions\MatrixRequestException; 6use function GuzzleHttp\default_ca_bundle; 7use http\Exception; 8use phpDocumentor\Reflection\DocBlock\Tags\Param; 9 10/** 11 * Call room-specific functions after joining a room from the client. 12 * 13 * NOTE: This should ideally be called from within the Client. 14 * NOTE: This does not verify the room with the Home Server. 15 * 16 * @package MatrixPhp 17 */ 18class Room { 19 20 /** @var MatrixClient */ 21 protected $client; 22 protected $roomId; 23 protected $listeners = []; 24 protected $stateListeners = []; 25 protected $ephemeralListeners = []; 26 protected $events = []; 27 protected $eventHistoryLimit = 20; 28 protected $name; 29 protected $canonicalAlias; 30 protected $aliases = []; 31 protected $topic; 32 protected $inviteOnly = false; 33 protected $guestAccess; 34 public $prevBatch; 35 protected $_members = []; 36 protected $membersDisplaynames = [ 37 // $userId: $displayname, 38 ]; 39 protected $encrypted = false; 40 41 public function __construct(MatrixClient $client, string $roomId) { 42 Util::checkRoomId($roomId); 43 $this->roomId = $roomId; 44 $this->client = $client; 45 } 46 47 /** 48 * Set user profile within a room. 49 * 50 * This sets displayname and avatar_url for the logged in user only in a 51 * specific room. It does not change the user's global user profile. 52 * 53 * @param string|null $displayname 54 * @param string|null $avatarUrl 55 * @param string $reason 56 * @throws Exceptions\MatrixException 57 * @throws Exceptions\MatrixHttpLibException 58 * @throws Exceptions\MatrixRequestException 59 */ 60 public function setUserProfile(?string $displayname = null, ?string $avatarUrl = null, 61 string $reason = "Changing room profile information") { 62 $member = $this->api()->getMembership($this->roomId, $this->client->userId()); 63 if ($member['membership'] != 'join') { 64 throw new \Exception("Can't set profile if you have not joined the room."); 65 } 66 if (!$displayname) { 67 $displayname = $member["displayname"]; 68 } 69 if (!$avatarUrl) { 70 $avatarUrl = $member["avatar_url"]; 71 } 72 $this->api()->setMembership( 73 $this->roomId, 74 $this->client->userId(), 75 'join', 76 $reason, 77 [ 78 "displayname" => $displayname, 79 "avatar_url" => $avatarUrl 80 ] 81 ); 82 } 83 84 /** 85 * Calculates the display name for a room. 86 * 87 * @return string 88 */ 89 public function displayName() { 90 if ($this->name) { 91 return $this->name; 92 } elseif ($this->canonicalAlias) { 93 return $this->canonicalAlias; 94 } 95 96 // Member display names without me 97 $members = array_reduce($this->getJoinedMembers(), function (array $all, User $u) { 98 if ($this->client->userId() != $u->userId()) { 99 $all[] = $u->getDisplayName($this); 100 } 101 return $all; 102 }, []); 103 sort($members); 104 105 switch (count($members)) { 106 case 0: 107 return 'Empty room'; 108 case 1: 109 return $members[0]; 110 case 2: 111 return sprintf("%s and %s", $members[0], $members[1]); 112 default: 113 return sprintf("%s and %d others.", $members[0], count($members)); 114 } 115 } 116 117 /** 118 * Send a plain text message to the room. 119 * 120 * @param string $text 121 * @return array|string 122 * @throws Exceptions\MatrixException 123 * @throws Exceptions\MatrixHttpLibException 124 * @throws Exceptions\MatrixRequestException 125 */ 126 public function sendText(string $text) { 127 return $this->api()->sendMessage($this->roomId, $text); 128 } 129 130 public function getHtmlContent(string $html, ?string $body = null, string $msgType = 'm.text') { 131 return [ 132 'body' => $body ?: strip_tags($html), 133 'msgtype' => $msgType, 134 'format' => "org.matrix.custom.html", 135 'formatted_body' => $html, 136 ]; 137 } 138 139 /** 140 * Send an html formatted message. 141 * 142 * @param string $html The html formatted message to be sent. 143 * @param string|null $body The unformatted body of the message to be sent. 144 * @param string $msgType 145 * @return array|string 146 * @throws Exceptions\MatrixException 147 * @throws Exceptions\MatrixHttpLibException 148 * @throws Exceptions\MatrixRequestException 149 */ 150 public function sendHtml(string $html, ?string $body = null, string $msgType = 'm.text') { 151 $content = $this->getHtmlContent($html, $body, $msgType); 152 153 return $this->api()->sendMessageEvent($this->roomId, 'm.room.message', $content); 154 } 155 156 /** 157 * @param string $type 158 * @param array $data 159 * @return array|string 160 * @throws Exceptions\MatrixException 161 * @throws Exceptions\MatrixHttpLibException 162 * @throws Exceptions\MatrixRequestException 163 */ 164 public function setAccountData(string $type, array $data) { 165 return $this->api()->setRoomAccountData($this->client->userId(), $this->roomId, $type, $data); 166 } 167 168 /** 169 * @return array|string 170 * @throws Exceptions\MatrixException 171 * @throws Exceptions\MatrixHttpLibException 172 * @throws Exceptions\MatrixRequestException 173 */ 174 public function getTags() { 175 return $this->api()->getUserTags($this->client->userId(), $this->roomId); 176 } 177 178 /** 179 * @param string $tag 180 * @return array|string 181 * @throws Exceptions\MatrixException 182 * @throws Exceptions\MatrixHttpLibException 183 * @throws Exceptions\MatrixRequestException 184 */ 185 public function removeTag(string $tag) { 186 return $this->api()->removeUserTag($this->client->userId(), $this->roomId, $tag); 187 } 188 189 /** 190 * @param string $tag 191 * @param float|null $order 192 * @param array $content 193 * @return array|string 194 * @throws Exceptions\MatrixException 195 * @throws Exceptions\MatrixHttpLibException 196 * @throws Exceptions\MatrixRequestException 197 */ 198 public function addTag(string $tag, ?float $order = null, array $content = []) { 199 return $this->api()->addUserTag($this->client->userId(), $this->roomId, $tag, $order, $content); 200 } 201 202 /** 203 * Send an emote (/me style) message to the room. 204 * 205 * @param string $text 206 * @return array|string 207 * @throws Exceptions\MatrixException 208 * @throws Exceptions\MatrixHttpLibException 209 * @throws Exceptions\MatrixRequestException 210 */ 211 public function sendEmote(string $text) { 212 return $this->api()->sendEmote($this->roomId, $text); 213 } 214 215 /** 216 * Send a pre-uploaded file to the room. 217 * 218 * See http://matrix.org/docs/spec/r0.4.0/client_server.html#m-file for fileinfo. 219 * 220 * @param string $url The mxc url of the file. 221 * @param string $name The filename of the image. 222 * @param array $fileinfo Extra information about the file 223 * @return array|string 224 * @throws Exceptions\MatrixException 225 * @throws Exceptions\MatrixHttpLibException 226 * @throws Exceptions\MatrixRequestException 227 */ 228 public function sendFile(string $url, string $name, array $fileinfo) { 229 return $this->api()->sendContent($this->roomId, $url, $name, 'm.file', $fileinfo); 230 } 231 232 /** 233 * Send a notice (from bot) message to the room. 234 * 235 * @param string $text 236 * @return array|string 237 * @throws Exceptions\MatrixException 238 * @throws Exceptions\MatrixHttpLibException 239 * @throws Exceptions\MatrixRequestException 240 */ 241 public function sendNotice(string $text) { 242 return $this->api()->sendNotice($this->roomId, $text); 243 } 244 245 /** 246 * Send a pre-uploaded image to the room. 247 * 248 * See http://matrix.org/docs/spec/r0.0.1/client_server.html#m-image for imageinfo 249 * 250 * @param string $url The mxc url of the image. 251 * @param string $name The filename of the image. 252 * @param array $fileinfo Extra information about the image. 253 * @return array|string 254 * @throws Exceptions\MatrixException 255 * @throws Exceptions\MatrixHttpLibException 256 * @throws Exceptions\MatrixRequestException 257 */ 258 public function sendImage(string $url, string $name, ?array $fileinfo) { 259 return $this->api()->sendContent($this->roomId, $url, $name, 'm.image', $fileinfo); 260 } 261 262 /** 263 * Send a location to the room. 264 * See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location for thumb_info 265 * 266 * @param string $geoUri The geo uri representing the location. 267 * @param string $name Description for the location. 268 * @param array $thumbInfo Metadata about the thumbnail, type ImageInfo. 269 * @param string|null $thumbUrl URL to the thumbnail of the location. 270 * @return array|string 271 * @throws Exceptions\MatrixException 272 * @throws Exceptions\MatrixHttpLibException 273 * @throws Exceptions\MatrixRequestException 274 */ 275 public function sendLocation(string $geoUri, string $name, ?array $thumbInfo, ?string $thumbUrl = null) { 276 return $this->api()->sendLocation($this->roomId, $geoUri, $name, $thumbUrl, $thumbInfo); 277 } 278 279 /** 280 * Send a pre-uploaded video to the room. 281 * See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video for videoinfo 282 * 283 * @param string $url The mxc url of the video. 284 * @param string $name The filename of the video. 285 * @param array $videoinfo Extra information about the video. 286 * @return array|string 287 * @throws Exceptions\MatrixException 288 * @throws Exceptions\MatrixHttpLibException 289 * @throws Exceptions\MatrixRequestException 290 */ 291 public function sendVideo(string $url, string $name, ?array $videoinfo) { 292 return $this->api()->sendContent($this->roomId, $url, $name, 'm.video', $videoinfo); 293 } 294 295 /** 296 * Send a pre-uploaded audio to the room. 297 * See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio for audioinfo 298 * 299 * @param string $url The mxc url of the video. 300 * @param string $name The filename of the video. 301 * @param array $audioinfo Extra information about the video. 302 * @return array|string 303 * @throws Exceptions\MatrixException 304 * @throws Exceptions\MatrixHttpLibException 305 * @throws Exceptions\MatrixRequestException 306 */ 307 public function sendAudio(string $url, string $name, ?array $audioinfo) { 308 return $this->api()->sendContent($this->roomId, $url, $name, 'm.audio', $audioinfo); 309 } 310 311 /** 312 * Redacts the message with specified event_id for the given reason. 313 * 314 * See https://matrix.org/docs/spec/r0.0.1/client_server.html#id112 315 * 316 * @param string $eventId 317 * @param string|null $reason 318 * @return array|string 319 * @throws Exceptions\MatrixException 320 * @throws Exceptions\MatrixHttpLibException 321 * @throws Exceptions\MatrixRequestException 322 */ 323 public function redactMessage(string $eventId, ?string $reason = null) { 324 return $this->api()->redactEvent($this->roomId, $eventId, $reason); 325 } 326 327 /** 328 * Add a callback handler for events going to this room. 329 * 330 * @param callable $cb (func(room, event)): Callback called when an event arrives. 331 * @param string|null $eventType The event_type to filter for. 332 * @return string Unique id of the listener, can be used to identify the listener. 333 */ 334 public function addListener(callable $cb, ?string $eventType = null) { 335 $listenerId = uniqid(); 336 $this->listeners[] = [ 337 'uid' => $listenerId, 338 'callback' => $cb, 339 'event_type' => $eventType, 340 ]; 341 342 return $listenerId; 343 } 344 345 /** 346 * Remove listener with given uid. 347 * 348 * @param string $uid 349 */ 350 public function removeListener(string $uid) { 351 $this->listeners = array_filter($this->listeners, function ($l) use ($uid) { 352 return $l['uid'] != $uid; 353 }); 354 } 355 356 /** 357 * Add a callback handler for ephemeral events going to this room. 358 * 359 * @param callable $cb (func(room, event)): Callback called when an ephemeral event arrives. 360 * @param string|null $eventType The event_type to filter for. 361 * @return string Unique id of the listener, can be used to identify the listener. 362 */ 363 public function addEphemeralListener(callable $cb, ?string $eventType = null) { 364 $listenerId = uniqid(); 365 $this->ephemeralListeners[] = [ 366 'uid' => $listenerId, 367 'callback' => $cb, 368 'event_type' => $eventType, 369 ]; 370 371 return $listenerId; 372 } 373 374 /** 375 * Remove ephemeral listener with given uid. 376 * 377 * @param string $uid 378 */ 379 public function removeEphemeralListener(string $uid) { 380 $this->ephemeralListeners = array_filter($this->ephemeralListeners, function ($l) use ($uid) { 381 return $l['uid'] != $uid; 382 }); 383 } 384 385 /** 386 * Add a callback handler for state events going to this room. 387 * 388 * @param callable $cb Callback called when an event arrives. 389 * @param string|null $eventType The event_type to filter for. 390 */ 391 public function addStateListener(callable $cb, ?string $eventType = null) { 392 $this->stateListeners[] = [ 393 'callback' => $cb, 394 'event_type' => $eventType, 395 ]; 396 } 397 398 public function putEvent(array $event) { 399 $this->events[] = $event; 400 if (count($this->events) > $this->eventHistoryLimit) { 401 array_pop($this->events); 402 } 403 if (array_key_exists('state_event', $event)) { 404 $this->processStateEvent($event); 405 } 406 // Dispatch for room-specific listeners 407 foreach ($this->listeners as $l) { 408 if (!$l['event_type'] || $l['event_type'] == $event['event_type']) { 409 $l['cb']($this, $event); 410 } 411 } 412 } 413 414 public function putEphemeralEvent(array $event) { 415 // Dispatch for room-specific listeners 416 foreach ($this->ephemeralListeners as $l) { 417 if (!$l['event_type'] || $l['event_type'] == $event['event_type']) { 418 $l['cb']($this, $event); 419 } 420 } 421 } 422 423 /** 424 * Get the most recent events for this room. 425 * 426 * @return array 427 */ 428 public function getEvents(): array { 429 return $this->events; 430 } 431 432 /** 433 * Invite a user to this room. 434 * 435 * @param string $userId 436 * @return bool Whether invitation was sent. 437 * @throws Exceptions\MatrixException 438 * @throws Exceptions\MatrixHttpLibException 439 */ 440 public function inviteUser(string $userId): bool { 441 try { 442 $this->api()->inviteUser($this->roomId, $userId); 443 } catch (MatrixRequestException $e) { 444 return false; 445 } 446 447 return true; 448 } 449 450 /** 451 * Kick a user from this room. 452 * 453 * @param string $userId The matrix user id of a user. 454 * @param string $reason A reason for kicking the user. 455 * @return bool Whether user was kicked. 456 * @throws Exceptions\MatrixException 457 */ 458 public function kickUser(string $userId, string $reason = ''): bool { 459 try { 460 $this->api()->kickUser($this->roomId, $userId, $reason); 461 } catch (MatrixRequestException $e) { 462 return false; 463 } 464 465 return true; 466 } 467 468 /** 469 * Ban a user from this room. 470 * 471 * @param string $userId The matrix user id of a user. 472 * @param string $reason A reason for banning the user. 473 * @return bool Whether user was banned. 474 * @throws Exceptions\MatrixException 475 */ 476 public function banUser(string $userId, string $reason = ''): bool { 477 try { 478 $this->api()->banUser($this->roomId, $userId, $reason); 479 } catch (MatrixRequestException $e) { 480 return false; 481 } 482 483 return true; 484 } 485 486 /** 487 * Leave the room. 488 * 489 * @return bool Leaving the room was successful. 490 * @throws Exceptions\MatrixException 491 * @throws Exceptions\MatrixHttpLibException 492 */ 493 public function leave() { 494 try { 495 $this->api()->leaveRoom($this->roomId); 496 $this->client->forgetRoom($this->roomId); 497 } catch (MatrixRequestException $e) { 498 return false; 499 } 500 501 return true; 502 } 503 504 /** 505 * Updates $this->name and returns true if room name has changed. 506 * @return bool 507 * @throws Exceptions\MatrixException 508 */ 509 public function updateRoomName() { 510 try { 511 $response = $this->api()->getRoomName($this->roomId); 512 $newName = array_get($response, 'name', $this->name); 513 $this->name = $newName; 514 if ($this->name != $newName) { 515 $this->name = $newName; 516 return true; 517 } 518 } catch (MatrixRequestException $e) { 519 } 520 521 return false; 522 } 523 524 /** 525 * Return True if room name successfully changed. 526 * 527 * @param string $name 528 * @return bool 529 * @throws Exceptions\MatrixException 530 */ 531 public function setRoomName(string $name) { 532 try { 533 $this->api()->setRoomName($this->roomId, $name); 534 $this->name = $name; 535 } catch (MatrixRequestException $e) { 536 return false; 537 } 538 539 return true; 540 } 541 542 /** 543 * Send a state event to the room. 544 * 545 * @param string $eventType The type of event that you are sending. 546 * @param array $content An object with the content of the message. 547 * @param string $stateKey Optional. A unique key to identify the state. 548 * @throws Exceptions\MatrixException 549 */ 550 public function sendStateEvent(string $eventType, array $content, string $stateKey = '') { 551 $this->api()->sendStateEvent($this->roomId, $eventType, $content, $stateKey); 552 } 553 554 /** 555 * Updates $this->topic and returns true if room topic has changed. 556 * 557 * @return bool 558 * @throws Exceptions\MatrixException 559 */ 560 public function updateRoomTopic() { 561 try { 562 $response = $this->api()->getRoomTopic($this->roomId); 563 $oldTopic = $this->topic; 564 $this->topic = array_get($response, 'topic', $this->topic); 565 } catch (MatrixRequestException $e) { 566 return false; 567 } 568 569 return $oldTopic == $this->topic; 570 } 571 572 /** 573 * Return True if room topic successfully changed. 574 * 575 * @param string $topic 576 * @return bool 577 * @throws Exceptions\MatrixException 578 */ 579 public function setRoomTopic(string $topic) { 580 try { 581 $this->api()->setRoomTopic($this->roomId, $topic); 582 $this->topic = $topic; 583 } catch (MatrixRequestException $e) { 584 return false; 585 } 586 587 return true; 588 } 589 590 /** 591 * Get aliases information from room state. 592 * 593 * @return bool True if the aliases changed, False if not 594 * @throws Exceptions\MatrixException 595 * @throws Exceptions\MatrixHttpLibException 596 */ 597 public function updateAliases() { 598 try { 599 $response = $this->api()->getRoomState($this->roomId); 600 $oldAliases = $this->aliases; 601 foreach ($response as $chunk) { 602 if ($aliases = array_get($chunk, 'content.aliases')) { 603 $this->aliases = $aliases; 604 return $this->aliases == $oldAliases; 605 } 606 } 607 } catch (MatrixRequestException $e) { 608 return false; 609 } 610 } 611 612 /** 613 * Add an alias to the room and return True if successful. 614 * 615 * @param string $alias 616 * @return bool 617 * @throws Exceptions\MatrixException 618 * @throws Exceptions\MatrixHttpLibException 619 */ 620 public function addRoomAlias(string $alias) { 621 try { 622 $this->api()->setRoomAlias($this->roomId, $alias); 623 } catch (MatrixRequestException $e) { 624 return false; 625 } 626 627 return true; 628 } 629 630 public function getJoinedMembers() { 631 if ($this->_members) { 632 return array_values($this->_members); 633 } 634 $response = $this->api()->getRoomMembers($this->roomId); 635 foreach ($response['chunk'] as $event) { 636 if (array_get($event, 'event.membership') == 'join') { 637 $userId = $event['state_key']; 638 $this->addMember($userId, array_get($event, 'content.displayname')); 639 } 640 } 641 642 return array_values($this->_members); 643 } 644 645 protected function addMember(string $userId, ?string $displayname) { 646 if ($displayname) { 647 $this->membersDisplaynames[$userId] = $displayname; 648 } 649 if (array_key_exists($userId, $this->_members)) { 650 return; 651 } 652 if (array_key_exists($userId, $this->client->users)) { 653 $this->_members[$userId] = $this->client->users[$userId]; 654 return; 655 } 656 $this->_members[$userId] = new User($this->api(), $userId, $displayname); 657 $this->client->users[$userId] = $this->_members[$userId]; 658 } 659 660 /** 661 * Backfill handling of previous messages. 662 * 663 * @param bool $reverse When false messages will be backfilled in their original 664 * order (old to new), otherwise the order will be reversed (new to old). 665 * @param int $limit Number of messages to go back. 666 * @throws Exceptions\MatrixException 667 * @throws Exceptions\MatrixHttpLibException 668 * @throws MatrixRequestException 669 */ 670 public function backfillPreviousMessages(bool $reverse = false, int $limit = 10) { 671 $res = $this->api()->getRoomMessages($this->roomId, $this->prevBatch, 'b', $limit); 672 $events = $res['chunk']; 673 if (!$reverse) { 674 $events = array_reverse($events); 675 } 676 foreach ($events as $event) { 677 $this->putEvent($event); 678 } 679 } 680 681 /** 682 * Modify the power level for a subset of users 683 * 684 * @param array $users Power levels to assign to specific users, in the form 685 * {"@name0:host0": 10, "@name1:host1": 100, "@name3:host3", None} 686 * A level of None causes the user to revert to the default level 687 * as specified by users_default. 688 * @param int $userDefault Default power level for users in the room 689 * @return bool 690 * @throws Exceptions\MatrixException 691 */ 692 public function modifyUserPowerLevels(array $users = null, int $userDefault = null) { 693 try { 694 $content = $this->api()->getPowerLevels($this->roomId); 695 if ($userDefault) { 696 $content['user_default'] = $userDefault; 697 } 698 699 if ($users) { 700 if (array_key_exists('users', $content)) { 701 $content['users'] = array_merge($content['users'], $content); 702 } else { 703 $content['users'] = $users; 704 } 705 706 // Remove any keys with value null 707 foreach ($content['users'] as $user => $pl) { 708 if (!$pl) { 709 unset($content['users'][$user]); 710 } 711 } 712 } 713 714 $this->api()->setPowerLevels($this->roomId, $content); 715 } catch (MatrixRequestException $e) { 716 return false; 717 } 718 719 return true; 720 } 721 722 /** 723 * Modifies room power level requirements. 724 * 725 * @param array $events Power levels required for sending specific event types, 726 * in the form {"m.room.whatever0": 60, "m.room.whatever2": None}. 727 * Overrides events_default and state_default for the specified 728 * events. A level of None causes the target event to revert to the 729 * default level as specified by events_default or state_default. 730 * @param array $extra Key/value pairs specifying the power levels required for 731 * various actions: 732 * 733 * - events_default(int): Default level for sending message events 734 * - state_default(int): Default level for sending state events 735 * - invite(int): Inviting a user 736 * - redact(int): Redacting an event 737 * - ban(int): Banning a user 738 * - kick(int): Kicking a user 739 * @return bool 740 * @throws Exceptions\MatrixException 741 */ 742 public function modifyRequiredPowerLevels(array $events = [], array $extra = []) { 743 try { 744 $content = $this->api()->getPowerLevels($this->roomId); 745 $content = array_merge($content, $extra); 746 foreach ($content as $k => $v) { 747 if (!$v) { 748 unset($content[$k]); 749 } 750 } 751 752 if ($events) { 753 if (array_key_exists('events', $content)) { 754 $content["events"] = array_merge($content["events"], $events); 755 } else { 756 $content["events"] = $events; 757 } 758 759 // Remove any keys with value null 760 foreach ($content['event'] as $event => $pl) { 761 if (!$pl) { 762 unset($content['event'][$event]); 763 } 764 } 765 } 766 767 $this->api()->setPowerLevels($this->roomId, $content); 768 } catch (MatrixRequestException $e) { 769 return false; 770 } 771 772 return true; 773 } 774 775 /** 776 * Set how the room can be joined. 777 * 778 * @param bool $inviteOnly If True, users will have to be invited to join 779 * the room. If False, anyone who knows the room link can join. 780 * @return bool True if successful, False if not 781 * @throws Exceptions\MatrixException 782 */ 783 public function setInviteOnly(bool $inviteOnly) { 784 $joinRule = $inviteOnly ? 'invite' : 'public'; 785 try { 786 $this->api()->setJoinRule($this->roomId, $joinRule); 787 $this->inviteOnly = $inviteOnly; 788 } catch (MatrixRequestException $e) { 789 return false; 790 } 791 792 return true; 793 } 794 795 /** 796 * Set whether guests can join the room and return True if successful. 797 * 798 * @param bool $allowGuest 799 * @return bool 800 * @throws Exceptions\MatrixException 801 */ 802 public function setGuestAccess(bool $allowGuest) { 803 $guestAccess = $allowGuest ? 'can_join' : 'forbidden'; 804 try { 805 $this->api()->setGuestAccess($this->roomId, $guestAccess); 806 $this->guestAccess = $allowGuest; 807 } catch (MatrixRequestException $e) { 808 return false; 809 } 810 811 return true; 812 } 813 814 /** 815 * Enables encryption in the room. 816 * 817 * NOTE: Once enabled, encryption cannot be disabled. 818 * 819 * @return bool True if successful, False if not 820 * @throws Exceptions\MatrixException 821 */ 822 public function enableEncryption() { 823 try { 824 $this->sendStateEvent('m.room.encryption', ['algorithm' => 'm.megolm.v1.aes-sha2']); 825 $this->encrypted = true; 826 } catch (MatrixRequestException $e) { 827 return false; 828 } 829 830 return true; 831 } 832 833 public function processStateEvent(array $stateEvent) { 834 if (!array_key_exists('type', $stateEvent)) { 835 return; 836 } 837 $etype = $stateEvent['type']; 838 $econtent = $stateEvent['content']; 839 $clevel = $this->client->cacheLevel(); 840 841 // Don't keep track of room state if caching turned off 842 if ($clevel >= Cache::SOME) { 843 switch ($etype) { 844 case 'm.room.name': 845 $this->name = array_get($econtent, 'name'); 846 break; 847 case 'm.room.canonical_alias': 848 $this->canonicalAlias = array_get($econtent, 'alias'); 849 break; 850 case 'm.room.topic': 851 $this->topic = array_get($econtent, 'topic'); 852 break; 853 case 'm.room.aliases': 854 $this->aliases = array_get($econtent, 'aliases'); 855 break; 856 case 'm.room.join_rules': 857 $this->inviteOnly = $econtent["join_rule"] == "invite"; 858 break; 859 case 'm.room.guest_access': 860 $this->guestAccess = $econtent["guest_access"] == "can_join"; 861 break; 862 case 'm.room.encryption': 863 $this->encrypted = array_get($econtent, 'algorithm') ? true : $this->encrypted; 864 break; 865 case 'm.room.member': 866 // tracking room members can be large e.g. #matrix:matrix.org 867 if ($clevel == Cache::ALL) { 868 if ($econtent['membership'] == 'join') { 869 $userId = $stateEvent['state_key']; 870 $this->addMember($userId, array_get($econtent, 'displayname')); 871 } elseif (in_array($econtent["membership"], ["leave", "kick", "invite"])) { 872 unset($this->_members[array_get($stateEvent, 'state_key')]); 873 } 874 } 875 break; 876 } 877 } 878 879 foreach ($this->stateListeners as $listener) { 880 if (!$listener['event_type'] || $listener['event_type'] == $stateEvent['type']) { 881 $listener['cb']($stateEvent); 882 } 883 } 884 } 885 886 public function getMembersDisplayNames(): array { 887 return $this->membersDisplaynames; 888 } 889 890 protected function api(): MatrixHttpApi { 891 return $this->client->api(); 892 } 893 894 895} 896