mantis-matrix-integration – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | <?php |
2 | |||
3 | namespace MatrixPhp; |
||
4 | |||
5 | use MatrixPhp\Exceptions\MatrixRequestException; |
||
6 | use function GuzzleHttp\default_ca_bundle; |
||
7 | use http\Exception; |
||
8 | use 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 | */ |
||
18 | class 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 | // @todo This is not implemented from the original library? |
||
497 | // $this->client->forgetRoom($this->roomId); |
||
498 | } catch (MatrixRequestException $e) { |
||
499 | return false; |
||
500 | } |
||
501 | |||
502 | return true; |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * Updates $this->name and returns true if room name has changed. |
||
507 | * @return bool |
||
508 | * @throws Exceptions\MatrixException |
||
509 | */ |
||
510 | public function updateRoomName() { |
||
511 | try { |
||
512 | $response = $this->api()->getRoomName($this->roomId); |
||
513 | $newName = array_get($response, 'name', $this->name); |
||
514 | $this->name = $newName; |
||
515 | if ($this->name != $newName) { |
||
516 | $this->name = $newName; |
||
517 | return true; |
||
518 | } |
||
519 | } catch (MatrixRequestException $e) { |
||
520 | } |
||
521 | |||
522 | return false; |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Return True if room name successfully changed. |
||
527 | * |
||
528 | * @param string $name |
||
529 | * @return bool |
||
530 | * @throws Exceptions\MatrixException |
||
531 | */ |
||
532 | public function setRoomName(string $name) { |
||
533 | try { |
||
534 | $this->api()->setRoomName($this->roomId, $name); |
||
535 | $this->name = $name; |
||
536 | } catch (MatrixRequestException $e) { |
||
537 | return false; |
||
538 | } |
||
539 | |||
540 | return true; |
||
541 | } |
||
542 | |||
543 | /** |
||
544 | * Send a state event to the room. |
||
545 | * |
||
546 | * @param string $eventType The type of event that you are sending. |
||
547 | * @param array $content An object with the content of the message. |
||
548 | * @param string $stateKey Optional. A unique key to identify the state. |
||
549 | * @throws Exceptions\MatrixException |
||
550 | */ |
||
551 | public function sendStateEvent(string $eventType, array $content, string $stateKey = '') { |
||
552 | $this->api()->sendStateEvent($this->roomId, $eventType, $content, $stateKey); |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Updates $this->topic and returns true if room topic has changed. |
||
557 | * |
||
558 | * @return bool |
||
559 | * @throws Exceptions\MatrixException |
||
560 | */ |
||
561 | public function updateRoomTopic() { |
||
562 | try { |
||
563 | $response = $this->api()->getRoomTopic($this->roomId); |
||
564 | $oldTopic = $this->topic; |
||
565 | $this->topic = array_get($response, 'topic', $this->topic); |
||
566 | } catch (MatrixRequestException $e) { |
||
567 | return false; |
||
568 | } |
||
569 | |||
570 | return $oldTopic == $this->topic; |
||
571 | } |
||
572 | |||
573 | /** |
||
574 | * Return True if room topic successfully changed. |
||
575 | * |
||
576 | * @param string $topic |
||
577 | * @return bool |
||
578 | * @throws Exceptions\MatrixException |
||
579 | */ |
||
580 | public function setRoomTopic(string $topic) { |
||
581 | try { |
||
582 | $this->api()->setRoomTopic($this->roomId, $topic); |
||
583 | $this->topic = $topic; |
||
584 | } catch (MatrixRequestException $e) { |
||
585 | return false; |
||
586 | } |
||
587 | |||
588 | return true; |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * Get aliases information from room state. |
||
593 | * |
||
594 | * @return bool True if the aliases changed, False if not |
||
595 | * @throws Exceptions\MatrixException |
||
596 | * @throws Exceptions\MatrixHttpLibException |
||
597 | */ |
||
598 | public function updateAliases() { |
||
599 | try { |
||
600 | $response = $this->api()->getRoomState($this->roomId); |
||
601 | $oldAliases = $this->aliases; |
||
602 | foreach ($response as $chunk) { |
||
603 | if ($aliases = array_get($chunk, 'content.aliases')) { |
||
604 | $this->aliases = $aliases; |
||
605 | } |
||
606 | } |
||
607 | } catch (MatrixRequestException $e) { |
||
608 | return false; |
||
609 | } |
||
610 | // @todo Validate this logic. |
||
611 | return $this->aliases !== $oldAliases; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Add an alias to the room and return True if successful. |
||
616 | * |
||
617 | * @param string $alias |
||
618 | * @return bool |
||
619 | * @throws Exceptions\MatrixException |
||
620 | * @throws Exceptions\MatrixHttpLibException |
||
621 | */ |
||
622 | public function addRoomAlias(string $alias) { |
||
623 | try { |
||
624 | $this->api()->setRoomAlias($this->roomId, $alias); |
||
625 | } catch (MatrixRequestException $e) { |
||
626 | return false; |
||
627 | } |
||
628 | |||
629 | return true; |
||
630 | } |
||
631 | |||
632 | public function getJoinedMembers() { |
||
633 | if ($this->_members) { |
||
634 | return array_values($this->_members); |
||
635 | } |
||
636 | $response = $this->api()->getRoomMembers($this->roomId); |
||
637 | foreach ($response['chunk'] as $event) { |
||
638 | if (array_get($event, 'event.membership') == 'join') { |
||
639 | $userId = $event['state_key']; |
||
640 | $this->addMember($userId, array_get($event, 'content.displayname')); |
||
641 | } |
||
642 | } |
||
643 | |||
644 | return array_values($this->_members); |
||
645 | } |
||
646 | |||
647 | protected function addMember(string $userId, ?string $displayname) { |
||
648 | if ($displayname) { |
||
649 | $this->membersDisplaynames[$userId] = $displayname; |
||
650 | } |
||
651 | if (array_key_exists($userId, $this->_members)) { |
||
652 | return; |
||
653 | } |
||
654 | if (array_key_exists($userId, $this->client->users)) { |
||
655 | $this->_members[$userId] = $this->client->users[$userId]; |
||
656 | return; |
||
657 | } |
||
658 | $this->_members[$userId] = new User($this->api(), $userId, $displayname); |
||
659 | $this->client->users[$userId] = $this->_members[$userId]; |
||
660 | } |
||
661 | |||
662 | /** |
||
663 | * Backfill handling of previous messages. |
||
664 | * |
||
665 | * @param bool $reverse When false messages will be backfilled in their original |
||
666 | * order (old to new), otherwise the order will be reversed (new to old). |
||
667 | * @param int $limit Number of messages to go back. |
||
668 | * @throws Exceptions\MatrixException |
||
669 | * @throws Exceptions\MatrixHttpLibException |
||
670 | * @throws MatrixRequestException |
||
671 | */ |
||
672 | public function backfillPreviousMessages(bool $reverse = false, int $limit = 10) { |
||
673 | $res = $this->api()->getRoomMessages($this->roomId, $this->prevBatch, 'b', $limit); |
||
674 | $events = $res['chunk']; |
||
675 | if (!$reverse) { |
||
676 | $events = array_reverse($events); |
||
677 | } |
||
678 | foreach ($events as $event) { |
||
679 | $this->putEvent($event); |
||
680 | } |
||
681 | } |
||
682 | |||
683 | /** |
||
684 | * Modify the power level for a subset of users |
||
685 | * |
||
686 | * @param array $users Power levels to assign to specific users, in the form |
||
687 | * {"@name0:host0": 10, "@name1:host1": 100, "@name3:host3", None} |
||
688 | * A level of None causes the user to revert to the default level |
||
689 | * as specified by users_default. |
||
690 | * @param int $userDefault Default power level for users in the room |
||
691 | * @return bool |
||
692 | * @throws Exceptions\MatrixException |
||
693 | */ |
||
694 | public function modifyUserPowerLevels(array $users = null, int $userDefault = null) { |
||
695 | try { |
||
696 | $content = $this->api()->getPowerLevels($this->roomId); |
||
697 | if ($userDefault) { |
||
698 | $content['user_default'] = $userDefault; |
||
699 | } |
||
700 | |||
701 | if ($users) { |
||
702 | if (array_key_exists('users', $content)) { |
||
703 | $content['users'] = array_merge($content['users'], $content); |
||
704 | } else { |
||
705 | $content['users'] = $users; |
||
706 | } |
||
707 | |||
708 | // Remove any keys with value null |
||
709 | foreach ($content['users'] as $user => $pl) { |
||
710 | if (!$pl) { |
||
711 | unset($content['users'][$user]); |
||
712 | } |
||
713 | } |
||
714 | } |
||
715 | |||
716 | $this->api()->setPowerLevels($this->roomId, $content); |
||
717 | } catch (MatrixRequestException $e) { |
||
718 | return false; |
||
719 | } |
||
720 | |||
721 | return true; |
||
722 | } |
||
723 | |||
724 | /** |
||
725 | * Modifies room power level requirements. |
||
726 | * |
||
727 | * @param array $events Power levels required for sending specific event types, |
||
728 | * in the form {"m.room.whatever0": 60, "m.room.whatever2": None}. |
||
729 | * Overrides events_default and state_default for the specified |
||
730 | * events. A level of None causes the target event to revert to the |
||
731 | * default level as specified by events_default or state_default. |
||
732 | * @param array $extra Key/value pairs specifying the power levels required for |
||
733 | * various actions: |
||
734 | * |
||
735 | * - events_default(int): Default level for sending message events |
||
736 | * - state_default(int): Default level for sending state events |
||
737 | * - invite(int): Inviting a user |
||
738 | * - redact(int): Redacting an event |
||
739 | * - ban(int): Banning a user |
||
740 | * - kick(int): Kicking a user |
||
741 | * @return bool |
||
742 | * @throws Exceptions\MatrixException |
||
743 | */ |
||
744 | public function modifyRequiredPowerLevels(array $events = [], array $extra = []) { |
||
745 | try { |
||
746 | $content = $this->api()->getPowerLevels($this->roomId); |
||
747 | $content = array_merge($content, $extra); |
||
748 | foreach ($content as $k => $v) { |
||
749 | if (!$v) { |
||
750 | unset($content[$k]); |
||
751 | } |
||
752 | } |
||
753 | |||
754 | if ($events) { |
||
755 | if (array_key_exists('events', $content)) { |
||
756 | $content["events"] = array_merge($content["events"], $events); |
||
757 | } else { |
||
758 | $content["events"] = $events; |
||
759 | } |
||
760 | |||
761 | // Remove any keys with value null |
||
762 | foreach ($content['event'] as $event => $pl) { |
||
763 | if (!$pl) { |
||
764 | unset($content['event'][$event]); |
||
765 | } |
||
766 | } |
||
767 | } |
||
768 | |||
769 | $this->api()->setPowerLevels($this->roomId, $content); |
||
770 | } catch (MatrixRequestException $e) { |
||
771 | return false; |
||
772 | } |
||
773 | |||
774 | return true; |
||
775 | } |
||
776 | |||
777 | /** |
||
778 | * Set how the room can be joined. |
||
779 | * |
||
780 | * @param bool $inviteOnly If True, users will have to be invited to join |
||
781 | * the room. If False, anyone who knows the room link can join. |
||
782 | * @return bool True if successful, False if not |
||
783 | * @throws Exceptions\MatrixException |
||
784 | */ |
||
785 | public function setInviteOnly(bool $inviteOnly) { |
||
786 | $joinRule = $inviteOnly ? 'invite' : 'public'; |
||
787 | try { |
||
788 | $this->api()->setJoinRule($this->roomId, $joinRule); |
||
789 | $this->inviteOnly = $inviteOnly; |
||
790 | } catch (MatrixRequestException $e) { |
||
791 | return false; |
||
792 | } |
||
793 | |||
794 | return true; |
||
795 | } |
||
796 | |||
797 | /** |
||
798 | * Set whether guests can join the room and return True if successful. |
||
799 | * |
||
800 | * @param bool $allowGuest |
||
801 | * @return bool |
||
802 | * @throws Exceptions\MatrixException |
||
803 | */ |
||
804 | public function setGuestAccess(bool $allowGuest) { |
||
805 | $guestAccess = $allowGuest ? 'can_join' : 'forbidden'; |
||
806 | try { |
||
807 | $this->api()->setGuestAccess($this->roomId, $guestAccess); |
||
808 | $this->guestAccess = $allowGuest; |
||
809 | } catch (MatrixRequestException $e) { |
||
810 | return false; |
||
811 | } |
||
812 | |||
813 | return true; |
||
814 | } |
||
815 | |||
816 | /** |
||
817 | * Enables encryption in the room. |
||
818 | * |
||
819 | * NOTE: Once enabled, encryption cannot be disabled. |
||
820 | * |
||
821 | * @return bool True if successful, False if not |
||
822 | * @throws Exceptions\MatrixException |
||
823 | */ |
||
824 | public function enableEncryption() { |
||
825 | try { |
||
826 | $this->sendStateEvent('m.room.encryption', ['algorithm' => 'm.megolm.v1.aes-sha2']); |
||
827 | $this->encrypted = true; |
||
828 | } catch (MatrixRequestException $e) { |
||
829 | return false; |
||
830 | } |
||
831 | |||
832 | return true; |
||
833 | } |
||
834 | |||
835 | public function processStateEvent(array $stateEvent) { |
||
836 | if (!array_key_exists('type', $stateEvent)) { |
||
837 | return; |
||
838 | } |
||
839 | $etype = $stateEvent['type']; |
||
840 | $econtent = $stateEvent['content']; |
||
841 | $clevel = $this->client->cacheLevel(); |
||
842 | |||
843 | // Don't keep track of room state if caching turned off |
||
844 | if ($clevel >= Cache::SOME) { |
||
845 | switch ($etype) { |
||
846 | case 'm.room.name': |
||
847 | $this->name = array_get($econtent, 'name'); |
||
848 | break; |
||
849 | case 'm.room.canonical_alias': |
||
850 | $this->canonicalAlias = array_get($econtent, 'alias'); |
||
851 | break; |
||
852 | case 'm.room.topic': |
||
853 | $this->topic = array_get($econtent, 'topic'); |
||
854 | break; |
||
855 | case 'm.room.aliases': |
||
856 | $this->aliases = array_get($econtent, 'aliases'); |
||
857 | break; |
||
858 | case 'm.room.join_rules': |
||
859 | $this->inviteOnly = $econtent["join_rule"] == "invite"; |
||
860 | break; |
||
861 | case 'm.room.guest_access': |
||
862 | $this->guestAccess = $econtent["guest_access"] == "can_join"; |
||
863 | break; |
||
864 | case 'm.room.encryption': |
||
865 | $this->encrypted = array_get($econtent, 'algorithm') ? true : $this->encrypted; |
||
866 | break; |
||
867 | case 'm.room.member': |
||
868 | // tracking room members can be large e.g. #matrix:matrix.org |
||
869 | if ($clevel == Cache::ALL) { |
||
870 | if ($econtent['membership'] == 'join') { |
||
871 | $userId = $stateEvent['state_key']; |
||
872 | $this->addMember($userId, array_get($econtent, 'displayname')); |
||
873 | } elseif (in_array($econtent["membership"], ["leave", "kick", "invite"])) { |
||
874 | unset($this->_members[array_get($stateEvent, 'state_key')]); |
||
875 | } |
||
876 | } |
||
877 | break; |
||
878 | } |
||
879 | } |
||
880 | |||
881 | foreach ($this->stateListeners as $listener) { |
||
882 | if (!$listener['event_type'] || $listener['event_type'] == $stateEvent['type']) { |
||
883 | $listener['cb']($stateEvent); |
||
884 | } |
||
885 | } |
||
886 | } |
||
887 | |||
888 | public function getMembersDisplayNames(): array { |
||
889 | return $this->membersDisplaynames; |
||
890 | } |
||
891 | |||
892 | protected function api(): MatrixHttpApi { |
||
893 | return $this->client->api(); |
||
894 | } |
||
895 | |||
896 | |||
897 | } |