matrix_sdk/room/
mod.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! High-level room API
16
17use std::{
18    borrow::Borrow,
19    collections::{BTreeMap, HashMap},
20    ops::Deref,
21    sync::Arc,
22    time::Duration,
23};
24
25use async_stream::stream;
26use eyeball::SharedObservable;
27use futures_core::Stream;
28use futures_util::{
29    future::{try_join, try_join_all},
30    stream::FuturesUnordered,
31};
32use http::StatusCode;
33#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
34pub use identity_status_changes::IdentityStatusChanges;
35#[cfg(feature = "e2e-encryption")]
36use matrix_sdk_base::crypto::{DecryptionSettings, RoomEventDecryptionResult};
37#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
38use matrix_sdk_base::crypto::{IdentityStatusChange, RoomIdentityProvider, UserIdentity};
39use matrix_sdk_base::{
40    deserialized_responses::{
41        RawAnySyncOrStrippedState, RawSyncOrStrippedState, SyncOrStrippedState,
42    },
43    event_cache::store::media::IgnoreMediaRetentionPolicy,
44    media::MediaThumbnailSettings,
45    store::StateStoreExt,
46    ComposerDraft, RoomInfoNotableUpdateReasons, RoomMemberships, StateChanges, StateStoreDataKey,
47    StateStoreDataValue,
48};
49#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
50use matrix_sdk_common::BoxFuture;
51use matrix_sdk_common::{
52    deserialized_responses::TimelineEvent,
53    executor::{spawn, JoinHandle},
54    timeout::timeout,
55};
56use mime::Mime;
57#[cfg(feature = "e2e-encryption")]
58use ruma::events::{
59    room::encrypted::OriginalSyncRoomEncryptedEvent, AnySyncMessageLikeEvent, AnySyncTimelineEvent,
60    SyncMessageLikeEvent,
61};
62use ruma::{
63    api::client::{
64        config::{set_global_account_data, set_room_account_data},
65        context,
66        error::ErrorKind,
67        filter::LazyLoadOptions,
68        membership::{
69            ban_user, forget_room, get_member_events,
70            invite_user::{self, v3::InvitationRecipient},
71            kick_user, leave_room, unban_user, Invite3pid,
72        },
73        message::send_message_event,
74        read_marker::set_read_marker,
75        receipt::create_receipt,
76        redact::redact_event,
77        room::{get_room_event, report_content, report_room},
78        state::{get_state_events_for_key, send_state_event},
79        tag::{create_tag, delete_tag},
80        typing::create_typing_event::{self, v3::Typing},
81    },
82    assign,
83    events::{
84        beacon::BeaconEventContent,
85        beacon_info::BeaconInfoEventContent,
86        call::notify::{ApplicationType, CallNotifyEventContent, NotifyType},
87        direct::DirectEventContent,
88        marked_unread::{MarkedUnreadEventContent, UnstableMarkedUnreadEventContent},
89        receipt::{Receipt, ReceiptThread, ReceiptType},
90        room::{
91            avatar::{self, RoomAvatarEventContent},
92            encryption::RoomEncryptionEventContent,
93            history_visibility::HistoryVisibility,
94            member::{MembershipChange, SyncRoomMemberEvent},
95            message::{
96                AudioInfo, AudioMessageEventContent, FileInfo, FileMessageEventContent,
97                FormattedBody, ImageMessageEventContent, MessageType, RoomMessageEventContent,
98                UnstableAudioDetailsContentBlock, UnstableVoiceContentBlock, VideoInfo,
99                VideoMessageEventContent,
100            },
101            name::RoomNameEventContent,
102            pinned_events::RoomPinnedEventsEventContent,
103            power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
104            server_acl::RoomServerAclEventContent,
105            topic::RoomTopicEventContent,
106            ImageInfo, MediaSource, ThumbnailInfo,
107        },
108        space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
109        tag::{TagInfo, TagName},
110        typing::SyncTypingEvent,
111        AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, AnyTimelineEvent, EmptyStateKey,
112        Mentions, MessageLikeEventContent, MessageLikeEventType, OriginalSyncStateEvent,
113        RedactContent, RedactedStateEventContent, RoomAccountDataEvent,
114        RoomAccountDataEventContent, RoomAccountDataEventType, StateEventContent, StateEventType,
115        StaticEventContent, StaticStateEventContent, SyncStateEvent,
116    },
117    push::{Action, PushConditionRoomCtx},
118    serde::Raw,
119    time::Instant,
120    EventId, Int, MatrixToUri, MatrixUri, MxcUri, OwnedEventId, OwnedRoomId, OwnedServerName,
121    OwnedTransactionId, OwnedUserId, RoomId, TransactionId, UInt, UserId,
122};
123use serde::de::DeserializeOwned;
124use thiserror::Error;
125use tokio::sync::broadcast;
126use tokio_stream::StreamExt;
127use tracing::{debug, info, instrument, warn};
128
129use self::futures::{SendAttachment, SendMessageLikeEvent, SendRawMessageLikeEvent};
130pub use self::{
131    member::{RoomMember, RoomMemberRole},
132    messages::{EventWithContextResponse, Messages, MessagesOptions},
133};
134#[cfg(doc)]
135use crate::event_cache::EventCache;
136use crate::{
137    attachment::{AttachmentConfig, AttachmentInfo},
138    client::WeakClient,
139    config::RequestConfig,
140    error::{BeaconError, WrongRoomState},
141    event_cache::{self, EventCacheDropHandles, RoomEventCache},
142    event_handler::{EventHandler, EventHandlerDropGuard, EventHandlerHandle, SyncEvent},
143    live_location_share::ObservableLiveLocation,
144    media::{MediaFormat, MediaRequestParameters},
145    notification_settings::{IsEncrypted, IsOneToOne, RoomNotificationMode},
146    room::{
147        knock_requests::{KnockRequest, KnockRequestMemberInfo},
148        power_levels::{RoomPowerLevelChanges, RoomPowerLevelsExt},
149        privacy_settings::RoomPrivacySettings,
150    },
151    sync::RoomUpdate,
152    utils::{IntoRawMessageLikeEventContent, IntoRawStateEventContent},
153    BaseRoom, Client, Error, HttpResult, Result, RoomState, TransmissionProgress,
154};
155#[cfg(feature = "e2e-encryption")]
156use crate::{crypto::types::events::CryptoContextInfo, encryption::backups::BackupState};
157
158pub mod edit;
159pub mod futures;
160pub mod identity_status_changes;
161/// Contains code related to requests to join a room.
162pub mod knock_requests;
163mod member;
164mod messages;
165pub mod power_levels;
166
167/// Contains all the functionality for modifying the privacy settings in a room.
168pub mod privacy_settings;
169
170/// A struct containing methods that are common for Joined, Invited and Left
171/// Rooms
172#[derive(Debug, Clone)]
173pub struct Room {
174    inner: BaseRoom,
175    pub(crate) client: Client,
176}
177
178impl Deref for Room {
179    type Target = BaseRoom;
180
181    fn deref(&self) -> &Self::Target {
182        &self.inner
183    }
184}
185
186const TYPING_NOTICE_TIMEOUT: Duration = Duration::from_secs(4);
187const TYPING_NOTICE_RESEND_TIMEOUT: Duration = Duration::from_secs(3);
188
189impl Room {
190    /// Create a new `Room`
191    ///
192    /// # Arguments
193    /// * `client` - The client used to make requests.
194    ///
195    /// * `room` - The underlying room.
196    pub(crate) fn new(client: Client, room: BaseRoom) -> Self {
197        Self { inner: room, client }
198    }
199
200    /// Leave this room.
201    ///
202    /// Only invited and joined rooms can be left.
203    #[doc(alias = "reject_invitation")]
204    pub async fn leave(&self) -> Result<()> {
205        let state = self.state();
206        if state == RoomState::Left {
207            return Err(Error::WrongRoomState(WrongRoomState::new("Joined or Invited", state)));
208        }
209
210        let request = leave_room::v3::Request::new(self.inner.room_id().to_owned());
211        self.client.send(request).await?;
212        self.client.base_client().room_left(self.room_id()).await?;
213        Ok(())
214    }
215
216    /// Join this room.
217    ///
218    /// Only invited and left rooms can be joined via this method.
219    #[doc(alias = "accept_invitation")]
220    pub async fn join(&self) -> Result<()> {
221        let state = self.state();
222        if state == RoomState::Joined {
223            return Err(Error::WrongRoomState(WrongRoomState::new("Invited or Left", state)));
224        }
225
226        let prev_room_state = self.inner.state();
227
228        let mark_as_direct = prev_room_state == RoomState::Invited
229            && self.inner.is_direct().await.unwrap_or_else(|e| {
230                warn!(room_id = ?self.room_id(), "is_direct() failed: {e}");
231                false
232            });
233
234        self.client.join_room_by_id(self.room_id()).await?;
235
236        if mark_as_direct {
237            self.set_is_direct(true).await?;
238        }
239
240        Ok(())
241    }
242
243    /// Get the inner client saved in this room instance.
244    ///
245    /// Returns the client this room is part of.
246    pub fn client(&self) -> Client {
247        self.client.clone()
248    }
249
250    /// Get the sync state of this room, i.e. whether it was fully synced with
251    /// the server.
252    pub fn is_synced(&self) -> bool {
253        self.inner.is_state_fully_synced()
254    }
255
256    /// Gets the avatar of this room, if set.
257    ///
258    /// Returns the avatar.
259    /// If a thumbnail is requested no guarantee on the size of the image is
260    /// given.
261    ///
262    /// # Arguments
263    ///
264    /// * `format` - The desired format of the avatar.
265    ///
266    /// # Examples
267    ///
268    /// ```no_run
269    /// # use matrix_sdk::Client;
270    /// # use matrix_sdk::ruma::room_id;
271    /// # use matrix_sdk::media::MediaFormat;
272    /// # use url::Url;
273    /// # let homeserver = Url::parse("http://example.com").unwrap();
274    /// # async {
275    /// # let user = "example";
276    /// let client = Client::new(homeserver).await.unwrap();
277    /// client.matrix_auth().login_username(user, "password").send().await.unwrap();
278    /// let room_id = room_id!("!roomid:example.com");
279    /// let room = client.get_room(&room_id).unwrap();
280    /// if let Some(avatar) = room.avatar(MediaFormat::File).await.unwrap() {
281    ///     std::fs::write("avatar.png", avatar);
282    /// }
283    /// # };
284    /// ```
285    pub async fn avatar(&self, format: MediaFormat) -> Result<Option<Vec<u8>>> {
286        let Some(url) = self.avatar_url() else { return Ok(None) };
287        let request = MediaRequestParameters { source: MediaSource::Plain(url.to_owned()), format };
288        Ok(Some(self.client.media().get_media_content(&request, true).await?))
289    }
290
291    /// Sends a request to `/_matrix/client/r0/rooms/{room_id}/messages` and
292    /// returns a `Messages` struct that contains a chunk of room and state
293    /// events (`RoomEvent` and `AnyStateEvent`).
294    ///
295    /// With the encryption feature, messages are decrypted if possible. If
296    /// decryption fails for an individual message, that message is returned
297    /// undecrypted.
298    ///
299    /// # Examples
300    ///
301    /// ```no_run
302    /// use matrix_sdk::{room::MessagesOptions, Client};
303    /// # use matrix_sdk::ruma::{
304    /// #     api::client::filter::RoomEventFilter,
305    /// #     room_id,
306    /// # };
307    /// # use url::Url;
308    ///
309    /// # let homeserver = Url::parse("http://example.com").unwrap();
310    /// # async {
311    /// let options =
312    ///     MessagesOptions::backward().from("t47429-4392820_219380_26003_2265");
313    ///
314    /// let mut client = Client::new(homeserver).await.unwrap();
315    /// let room = client.get_room(room_id!("!roomid:example.com")).unwrap();
316    /// assert!(room.messages(options).await.is_ok());
317    /// # };
318    /// ```
319    #[instrument(skip_all, fields(room_id = ?self.inner.room_id(), ?options))]
320    pub async fn messages(&self, options: MessagesOptions) -> Result<Messages> {
321        let room_id = self.inner.room_id();
322        let request = options.into_request(room_id);
323        let http_response = self.client.send(request).await?;
324
325        #[allow(unused_mut)]
326        let mut response = Messages {
327            start: http_response.start,
328            end: http_response.end,
329            #[cfg(not(feature = "e2e-encryption"))]
330            chunk: http_response
331                .chunk
332                .into_iter()
333                .map(|raw| TimelineEvent::new(raw.cast()))
334                .collect(),
335            #[cfg(feature = "e2e-encryption")]
336            chunk: Vec::with_capacity(http_response.chunk.len()),
337            state: http_response.state,
338        };
339
340        #[cfg(feature = "e2e-encryption")]
341        for event in http_response.chunk {
342            let decrypted_event = if let Ok(AnySyncTimelineEvent::MessageLike(
343                AnySyncMessageLikeEvent::RoomEncrypted(SyncMessageLikeEvent::Original(_)),
344            )) = event.deserialize_as::<AnySyncTimelineEvent>()
345            {
346                if let Ok(event) = self.decrypt_event(event.cast_ref()).await {
347                    event
348                } else {
349                    TimelineEvent::new(event.cast())
350                }
351            } else {
352                TimelineEvent::new(event.cast())
353            };
354            response.chunk.push(decrypted_event);
355        }
356
357        if let Some(push_context) = self.push_context().await? {
358            let push_rules = self.client().account().push_rules().await?;
359
360            for event in &mut response.chunk {
361                event.push_actions =
362                    Some(push_rules.get_actions(event.raw(), &push_context).to_owned());
363            }
364        }
365
366        Ok(response)
367    }
368
369    /// Register a handler for events of a specific type, within this room.
370    ///
371    /// This method works the same way as [`Client::add_event_handler`], except
372    /// that the handler will only be called for events within this room. See
373    /// that method for more details on event handler functions.
374    ///
375    /// `room.add_event_handler(hdl)` is equivalent to
376    /// `client.add_room_event_handler(room_id, hdl)`. Use whichever one is more
377    /// convenient in your use case.
378    pub fn add_event_handler<Ev, Ctx, H>(&self, handler: H) -> EventHandlerHandle
379    where
380        Ev: SyncEvent + DeserializeOwned + Send + 'static,
381        H: EventHandler<Ev, Ctx>,
382    {
383        self.client.add_room_event_handler(self.room_id(), handler)
384    }
385
386    /// Subscribe to all updates for this room.
387    ///
388    /// The returned receiver will receive a new message for each sync response
389    /// that contains updates for this room.
390    pub fn subscribe_to_updates(&self) -> broadcast::Receiver<RoomUpdate> {
391        self.client.subscribe_to_room_updates(self.room_id())
392    }
393
394    /// Subscribe to typing notifications for this room.
395    ///
396    /// The returned receiver will receive a new vector of user IDs for each
397    /// sync response that contains 'm.typing' event. The current user ID will
398    /// be filtered out.
399    pub fn subscribe_to_typing_notifications(
400        &self,
401    ) -> (EventHandlerDropGuard, broadcast::Receiver<Vec<OwnedUserId>>) {
402        let (sender, receiver) = broadcast::channel(16);
403        let typing_event_handler_handle = self.client.add_room_event_handler(self.room_id(), {
404            let own_user_id = self.own_user_id().to_owned();
405            move |event: SyncTypingEvent| async move {
406                // Ignore typing notifications from own user.
407                let typing_user_ids = event
408                    .content
409                    .user_ids
410                    .into_iter()
411                    .filter(|user_id| *user_id != own_user_id)
412                    .collect();
413                // Ignore the result. It can only fail if there are no listeners.
414                let _ = sender.send(typing_user_ids);
415            }
416        });
417        let drop_guard = self.client().event_handler_drop_guard(typing_event_handler_handle);
418        (drop_guard, receiver)
419    }
420
421    /// Subscribe to updates about users who are in "pin violation" i.e. their
422    /// identity has changed and the user has not yet acknowledged this.
423    ///
424    /// The returned receiver will receive a new vector of
425    /// [`IdentityStatusChange`] each time a /keys/query response shows a
426    /// changed identity for a member of this room, or a sync shows a change
427    /// to the membership of an affected user. (Changes to the current user are
428    /// not directly included, but some changes to the current user's identity
429    /// can trigger changes to how we see other users' identities, which
430    /// will be included.)
431    ///
432    /// The first item in the stream provides the current state of the room:
433    /// each member of the room who is not in "pinned" or "verified" state will
434    /// be included (except the current user).
435    ///
436    /// If the `changed_to` property of an [`IdentityStatusChange`] is set to
437    /// `PinViolation` then a warning should be displayed to the user. If it is
438    /// set to `Pinned` then no warning should be displayed.
439    ///
440    /// Note that if a user who is in pin violation leaves the room, a `Pinned`
441    /// update is sent, to indicate that the warning should be removed, even
442    /// though the user's identity is not necessarily pinned.
443    #[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
444    pub async fn subscribe_to_identity_status_changes(
445        &self,
446    ) -> Result<impl Stream<Item = Vec<IdentityStatusChange>>> {
447        IdentityStatusChanges::create_stream(self.clone()).await
448    }
449
450    /// Returns a wrapping `TimelineEvent` for the input `AnyTimelineEvent`,
451    /// decrypted if needs be.
452    ///
453    /// Doesn't return an error `Result` when decryption failed; only logs from
454    /// the crypto crate will indicate so.
455    async fn try_decrypt_event(&self, event: Raw<AnyTimelineEvent>) -> Result<TimelineEvent> {
456        #[cfg(feature = "e2e-encryption")]
457        if let Ok(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
458            SyncMessageLikeEvent::Original(_),
459        ))) = event.deserialize_as::<AnySyncTimelineEvent>()
460        {
461            if let Ok(event) = self.decrypt_event(event.cast_ref()).await {
462                return Ok(event);
463            }
464        }
465
466        let mut event = TimelineEvent::new(event.cast());
467        event.push_actions = self.event_push_actions(event.raw()).await?;
468
469        Ok(event)
470    }
471
472    /// Fetch the event with the given `EventId` in this room.
473    ///
474    /// It uses the given [`RequestConfig`] if provided, or the client's default
475    /// one otherwise.
476    pub async fn event(
477        &self,
478        event_id: &EventId,
479        request_config: Option<RequestConfig>,
480    ) -> Result<TimelineEvent> {
481        let request =
482            get_room_event::v3::Request::new(self.room_id().to_owned(), event_id.to_owned());
483
484        let raw_event = self.client.send(request).with_request_config(request_config).await?.event;
485        let event = self.try_decrypt_event(raw_event).await?;
486
487        // Save the event into the event cache, if it's set up.
488        if let Ok((cache, _handles)) = self.event_cache().await {
489            cache.save_event(event.clone()).await;
490        }
491
492        Ok(event)
493    }
494
495    /// Fetch the event with the given `EventId` in this room, using the
496    /// `/context` endpoint to get more information.
497    pub async fn event_with_context(
498        &self,
499        event_id: &EventId,
500        lazy_load_members: bool,
501        context_size: UInt,
502        request_config: Option<RequestConfig>,
503    ) -> Result<EventWithContextResponse> {
504        let mut request =
505            context::get_context::v3::Request::new(self.room_id().to_owned(), event_id.to_owned());
506
507        request.limit = context_size;
508
509        if lazy_load_members {
510            request.filter.lazy_load_options =
511                LazyLoadOptions::Enabled { include_redundant_members: false };
512        }
513
514        let response = self.client.send(request).with_request_config(request_config).await?;
515
516        let target_event = if let Some(event) = response.event {
517            Some(self.try_decrypt_event(event).await?)
518        } else {
519            None
520        };
521
522        // Note: the joined future will fail if any future failed, but
523        // [`Self::try_decrypt_event`] doesn't hard-fail when there's a
524        // decryption error, so we should prevent against most bad cases here.
525        let (events_before, events_after) = try_join(
526            try_join_all(response.events_before.into_iter().map(|ev| self.try_decrypt_event(ev))),
527            try_join_all(response.events_after.into_iter().map(|ev| self.try_decrypt_event(ev))),
528        )
529        .await?;
530
531        // Save the loaded events into the event cache, if it's set up.
532        if let Ok((cache, _handles)) = self.event_cache().await {
533            let mut events_to_save: Vec<TimelineEvent> = Vec::new();
534            if let Some(event) = &target_event {
535                events_to_save.push(event.clone());
536            }
537
538            for event in &events_before {
539                events_to_save.push(event.clone());
540            }
541
542            for event in &events_after {
543                events_to_save.push(event.clone());
544            }
545
546            cache.save_events(events_to_save).await;
547        }
548
549        Ok(EventWithContextResponse {
550            event: target_event,
551            events_before,
552            events_after,
553            state: response.state,
554            prev_batch_token: response.start,
555            next_batch_token: response.end,
556        })
557    }
558
559    pub(crate) async fn request_members(&self) -> Result<()> {
560        self.client
561            .locks()
562            .members_request_deduplicated_handler
563            .run(self.room_id().to_owned(), async move {
564                let request = get_member_events::v3::Request::new(self.inner.room_id().to_owned());
565                let response = self
566                    .client
567                    .send(request.clone())
568                    .with_request_config(
569                        // In some cases it can take longer than 30s to load:
570                        // https://github.com/element-hq/synapse/issues/16872
571                        RequestConfig::new().timeout(Duration::from_secs(60)).retry_limit(3),
572                    )
573                    .await?;
574
575                // That's a large `Future`. Let's `Box::pin` to reduce its size on the stack.
576                Box::pin(self.client.base_client().receive_all_members(
577                    self.room_id(),
578                    &request,
579                    &response,
580                ))
581                .await?;
582
583                Ok(())
584            })
585            .await
586    }
587
588    async fn request_encryption_state(&self) -> Result<()> {
589        self.client
590            .locks()
591            .encryption_state_deduplicated_handler
592            .run(self.room_id().to_owned(), async move {
593                // Request the event from the server.
594                let request = get_state_events_for_key::v3::Request::new(
595                    self.room_id().to_owned(),
596                    StateEventType::RoomEncryption,
597                    "".to_owned(),
598                );
599                let response = match self.client.send(request).await {
600                    Ok(response) => {
601                        Some(response.content.deserialize_as::<RoomEncryptionEventContent>()?)
602                    }
603                    Err(err) if err.client_api_error_kind() == Some(&ErrorKind::NotFound) => None,
604                    Err(err) => return Err(err.into()),
605                };
606
607                let _sync_lock = self.client.base_client().sync_lock().lock().await;
608
609                // Persist the event and the fact that we requested it from the server in
610                // `RoomInfo`.
611                let mut room_info = self.clone_info();
612                room_info.mark_encryption_state_synced();
613                room_info.set_encryption_event(response.clone());
614                let mut changes = StateChanges::default();
615                changes.add_room(room_info.clone());
616
617                self.client.store().save_changes(&changes).await?;
618                self.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
619
620                Ok(())
621            })
622            .await
623    }
624
625    /// Check whether this room is encrypted. If the room encryption state is
626    /// not synced yet, it will send a request to fetch it.
627    ///
628    /// Returns true if the room is encrypted, otherwise false.
629    pub async fn is_encrypted(&self) -> Result<bool> {
630        if !self.is_encryption_state_synced() {
631            self.request_encryption_state().await?;
632        }
633
634        Ok(self.inner.is_encrypted())
635    }
636
637    /// Gets additional context info about the client crypto.
638    #[cfg(feature = "e2e-encryption")]
639    pub async fn crypto_context_info(&self) -> CryptoContextInfo {
640        let encryption = self.client.encryption();
641
642        let this_device_is_verified = match encryption.get_own_device().await {
643            Ok(Some(device)) => device.is_verified_with_cross_signing(),
644
645            // Should not happen, there will always be an own device
646            _ => true,
647        };
648
649        let backup_exists_on_server =
650            encryption.backups().exists_on_server().await.unwrap_or(false);
651
652        CryptoContextInfo {
653            device_creation_ts: encryption.device_creation_timestamp().await,
654            this_device_is_verified,
655            is_backup_configured: encryption.backups().state() == BackupState::Enabled,
656            backup_exists_on_server,
657        }
658    }
659
660    fn are_events_visible(&self) -> bool {
661        if let RoomState::Invited = self.inner.state() {
662            return matches!(
663                self.inner.history_visibility_or_default(),
664                HistoryVisibility::WorldReadable | HistoryVisibility::Invited
665            );
666        }
667
668        true
669    }
670
671    /// Sync the member list with the server.
672    ///
673    /// This method will de-duplicate requests if it is called multiple times in
674    /// quick succession, in that case the return value will be `None`. This
675    /// method does nothing if the members are already synced.
676    pub async fn sync_members(&self) -> Result<()> {
677        if !self.are_events_visible() {
678            return Ok(());
679        }
680
681        if !self.are_members_synced() {
682            self.request_members().await
683        } else {
684            Ok(())
685        }
686    }
687
688    /// Get a specific member of this room.
689    ///
690    /// *Note*: This method will fetch the members from the homeserver if the
691    /// member list isn't synchronized due to member lazy loading. Because of
692    /// that it might panic if it isn't run on a tokio thread.
693    ///
694    /// Use [get_member_no_sync()](#method.get_member_no_sync) if you want a
695    /// method that doesn't do any requests.
696    ///
697    /// # Arguments
698    ///
699    /// * `user_id` - The ID of the user that should be fetched out of the
700    ///   store.
701    pub async fn get_member(&self, user_id: &UserId) -> Result<Option<RoomMember>> {
702        self.sync_members().await?;
703        self.get_member_no_sync(user_id).await
704    }
705
706    /// Get a specific member of this room.
707    ///
708    /// *Note*: This method will not fetch the members from the homeserver if
709    /// the member list isn't synchronized due to member lazy loading. Thus,
710    /// members could be missing.
711    ///
712    /// Use [get_member()](#method.get_member) if you want to ensure to always
713    /// have the full member list to chose from.
714    ///
715    /// # Arguments
716    ///
717    /// * `user_id` - The ID of the user that should be fetched out of the
718    ///   store.
719    pub async fn get_member_no_sync(&self, user_id: &UserId) -> Result<Option<RoomMember>> {
720        Ok(self
721            .inner
722            .get_member(user_id)
723            .await?
724            .map(|member| RoomMember::new(self.client.clone(), member)))
725    }
726
727    /// Get members for this room, with the given memberships.
728    ///
729    /// *Note*: This method will fetch the members from the homeserver if the
730    /// member list isn't synchronized due to member lazy loading. Because of
731    /// that it might panic if it isn't run on a tokio thread.
732    ///
733    /// Use [members_no_sync()](#method.members_no_sync) if you want a
734    /// method that doesn't do any requests.
735    pub async fn members(&self, memberships: RoomMemberships) -> Result<Vec<RoomMember>> {
736        self.sync_members().await?;
737        self.members_no_sync(memberships).await
738    }
739
740    /// Get members for this room, with the given memberships.
741    ///
742    /// *Note*: This method will not fetch the members from the homeserver if
743    /// the member list isn't synchronized due to member lazy loading. Thus,
744    /// members could be missing.
745    ///
746    /// Use [members()](#method.members) if you want to ensure to always get
747    /// the full member list.
748    pub async fn members_no_sync(&self, memberships: RoomMemberships) -> Result<Vec<RoomMember>> {
749        Ok(self
750            .inner
751            .members(memberships)
752            .await?
753            .into_iter()
754            .map(|member| RoomMember::new(self.client.clone(), member))
755            .collect())
756    }
757
758    /// Get all state events of a given type in this room.
759    pub async fn get_state_events(
760        &self,
761        event_type: StateEventType,
762    ) -> Result<Vec<RawAnySyncOrStrippedState>> {
763        self.client.store().get_state_events(self.room_id(), event_type).await.map_err(Into::into)
764    }
765
766    /// Get all state events of a given statically-known type in this room.
767    ///
768    /// # Examples
769    ///
770    /// ```no_run
771    /// # async {
772    /// # let room: matrix_sdk::Room = todo!();
773    /// use matrix_sdk::ruma::{
774    ///     events::room::member::RoomMemberEventContent, serde::Raw,
775    /// };
776    ///
777    /// let room_members =
778    ///     room.get_state_events_static::<RoomMemberEventContent>().await?;
779    /// # anyhow::Ok(())
780    /// # };
781    /// ```
782    pub async fn get_state_events_static<C>(&self) -> Result<Vec<RawSyncOrStrippedState<C>>>
783    where
784        C: StaticEventContent + StaticStateEventContent + RedactContent,
785        C::Redacted: RedactedStateEventContent,
786    {
787        Ok(self.client.store().get_state_events_static(self.room_id()).await?)
788    }
789
790    /// Get the state events of a given type with the given state keys in this
791    /// room.
792    pub async fn get_state_events_for_keys(
793        &self,
794        event_type: StateEventType,
795        state_keys: &[&str],
796    ) -> Result<Vec<RawAnySyncOrStrippedState>> {
797        self.client
798            .store()
799            .get_state_events_for_keys(self.room_id(), event_type, state_keys)
800            .await
801            .map_err(Into::into)
802    }
803
804    /// Get the state events of a given statically-known type with the given
805    /// state keys in this room.
806    ///
807    /// # Examples
808    ///
809    /// ```no_run
810    /// # async {
811    /// # let room: matrix_sdk::Room = todo!();
812    /// # let user_ids: &[matrix_sdk::ruma::OwnedUserId] = &[];
813    /// use matrix_sdk::ruma::events::room::member::RoomMemberEventContent;
814    ///
815    /// let room_members = room
816    ///     .get_state_events_for_keys_static::<RoomMemberEventContent, _, _>(
817    ///         user_ids,
818    ///     )
819    ///     .await?;
820    /// # anyhow::Ok(())
821    /// # };
822    /// ```
823    pub async fn get_state_events_for_keys_static<'a, C, K, I>(
824        &self,
825        state_keys: I,
826    ) -> Result<Vec<RawSyncOrStrippedState<C>>>
827    where
828        C: StaticEventContent + StaticStateEventContent + RedactContent,
829        C::StateKey: Borrow<K>,
830        C::Redacted: RedactedStateEventContent,
831        K: AsRef<str> + Sized + Sync + 'a,
832        I: IntoIterator<Item = &'a K> + Send,
833        I::IntoIter: Send,
834    {
835        Ok(self.client.store().get_state_events_for_keys_static(self.room_id(), state_keys).await?)
836    }
837
838    /// Get a specific state event in this room.
839    pub async fn get_state_event(
840        &self,
841        event_type: StateEventType,
842        state_key: &str,
843    ) -> Result<Option<RawAnySyncOrStrippedState>> {
844        self.client
845            .store()
846            .get_state_event(self.room_id(), event_type, state_key)
847            .await
848            .map_err(Into::into)
849    }
850
851    /// Get a specific state event of statically-known type with an empty state
852    /// key in this room.
853    ///
854    /// # Examples
855    ///
856    /// ```no_run
857    /// # async {
858    /// # let room: matrix_sdk::Room = todo!();
859    /// use matrix_sdk::ruma::events::room::power_levels::RoomPowerLevelsEventContent;
860    ///
861    /// let power_levels = room
862    ///     .get_state_event_static::<RoomPowerLevelsEventContent>()
863    ///     .await?
864    ///     .expect("every room has a power_levels event")
865    ///     .deserialize()?;
866    /// # anyhow::Ok(())
867    /// # };
868    /// ```
869    pub async fn get_state_event_static<C>(&self) -> Result<Option<RawSyncOrStrippedState<C>>>
870    where
871        C: StaticEventContent + StaticStateEventContent<StateKey = EmptyStateKey> + RedactContent,
872        C::Redacted: RedactedStateEventContent,
873    {
874        self.get_state_event_static_for_key(&EmptyStateKey).await
875    }
876
877    /// Get a specific state event of statically-known type in this room.
878    ///
879    /// # Examples
880    ///
881    /// ```no_run
882    /// # async {
883    /// # let room: matrix_sdk::Room = todo!();
884    /// use matrix_sdk::ruma::{
885    ///     events::room::member::RoomMemberEventContent, serde::Raw, user_id,
886    /// };
887    ///
888    /// let member_event = room
889    ///     .get_state_event_static_for_key::<RoomMemberEventContent, _>(user_id!(
890    ///         "@alice:example.org"
891    ///     ))
892    ///     .await?;
893    /// # anyhow::Ok(())
894    /// # };
895    /// ```
896    pub async fn get_state_event_static_for_key<C, K>(
897        &self,
898        state_key: &K,
899    ) -> Result<Option<RawSyncOrStrippedState<C>>>
900    where
901        C: StaticEventContent + StaticStateEventContent + RedactContent,
902        C::StateKey: Borrow<K>,
903        C::Redacted: RedactedStateEventContent,
904        K: AsRef<str> + ?Sized + Sync,
905    {
906        Ok(self.client.store().get_state_event_static_for_key(self.room_id(), state_key).await?)
907    }
908
909    /// Returns the parents this room advertises as its parents.
910    ///
911    /// Results are in no particular order.
912    pub async fn parent_spaces(&self) -> Result<impl Stream<Item = Result<ParentSpace>> + '_> {
913        // Implements this algorithm:
914        // https://spec.matrix.org/v1.8/client-server-api/#mspaceparent-relationships
915
916        // Get all m.room.parent events for this room
917        Ok(self
918            .get_state_events_static::<SpaceParentEventContent>()
919            .await?
920            .into_iter()
921            // Extract state key (ie. the parent's id) and sender
922            .flat_map(|parent_event| match parent_event.deserialize() {
923                Ok(SyncOrStrippedState::Sync(SyncStateEvent::Original(e))) => {
924                    Some((e.state_key.to_owned(), e.sender))
925                }
926                Ok(SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_))) => None,
927                Ok(SyncOrStrippedState::Stripped(e)) => Some((e.state_key.to_owned(), e.sender)),
928                Err(e) => {
929                    info!(room_id = ?self.room_id(), "Could not deserialize m.room.parent: {e}");
930                    None
931                }
932            })
933            // Check whether the parent recognizes this room as its child
934            .map(|(state_key, sender): (OwnedRoomId, OwnedUserId)| async move {
935                let Some(parent_room) = self.client.get_room(&state_key) else {
936                    // We are not in the room, cannot check if the relationship is reciprocal
937                    // TODO: try peeking into the room
938                    return Ok(ParentSpace::Unverifiable(state_key));
939                };
940                // Get the m.room.child state of the parent with this room's id
941                // as state key.
942                if let Some(child_event) = parent_room
943                    .get_state_event_static_for_key::<SpaceChildEventContent, _>(self.room_id())
944                    .await?
945                {
946                    match child_event.deserialize() {
947                        Ok(SyncOrStrippedState::Sync(SyncStateEvent::Original(_))) => {
948                            // There is a valid m.room.child in the parent pointing to
949                            // this room
950                            return Ok(ParentSpace::Reciprocal(parent_room));
951                        }
952                        Ok(SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_))) => {}
953                        Ok(SyncOrStrippedState::Stripped(_)) => {}
954                        Err(e) => {
955                            info!(
956                                room_id = ?self.room_id(), parent_room_id = ?state_key,
957                                "Could not deserialize m.room.child: {e}"
958                            );
959                        }
960                    }
961                    // Otherwise the event is either invalid or redacted. If
962                    // redacted it would be missing the
963                    // `via` key, thereby invalidating that end of the
964                    // relationship: https://spec.matrix.org/v1.8/client-server-api/#mspacechild
965                }
966
967                // No reciprocal m.room.child found, let's check if the sender has the
968                // power to set it
969                let Some(member) = parent_room.get_member(&sender).await? else {
970                    // Sender is not even in the parent room
971                    return Ok(ParentSpace::Illegitimate(parent_room));
972                };
973
974                if member.can_send_state(StateEventType::SpaceChild) {
975                    // Sender does have the power to set m.room.child
976                    Ok(ParentSpace::WithPowerlevel(parent_room))
977                } else {
978                    Ok(ParentSpace::Illegitimate(parent_room))
979                }
980            })
981            .collect::<FuturesUnordered<_>>())
982    }
983
984    /// Read account data in this room, from storage.
985    pub async fn account_data(
986        &self,
987        data_type: RoomAccountDataEventType,
988    ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>> {
989        self.client
990            .store()
991            .get_room_account_data_event(self.room_id(), data_type)
992            .await
993            .map_err(Into::into)
994    }
995
996    /// Get account data of a statically-known type in this room, from storage.
997    ///
998    /// # Examples
999    ///
1000    /// ```no_run
1001    /// # async {
1002    /// # let room: matrix_sdk::Room = todo!();
1003    /// use matrix_sdk::ruma::events::fully_read::FullyReadEventContent;
1004    ///
1005    /// match room.account_data_static::<FullyReadEventContent>().await? {
1006    ///     Some(fully_read) => {
1007    ///         println!("Found read marker: {:?}", fully_read.deserialize()?)
1008    ///     }
1009    ///     None => println!("No read marker for this room"),
1010    /// }
1011    /// # anyhow::Ok(())
1012    /// # };
1013    /// ```
1014    pub async fn account_data_static<C>(&self) -> Result<Option<Raw<RoomAccountDataEvent<C>>>>
1015    where
1016        C: StaticEventContent + RoomAccountDataEventContent,
1017    {
1018        Ok(self.account_data(C::TYPE.into()).await?.map(Raw::cast))
1019    }
1020
1021    /// Check if all members of this room are verified and all their devices are
1022    /// verified.
1023    ///
1024    /// Returns true if all devices in the room are verified, otherwise false.
1025    #[cfg(feature = "e2e-encryption")]
1026    pub async fn contains_only_verified_devices(&self) -> Result<bool> {
1027        let user_ids =
1028            self.client.store().get_user_ids(self.room_id(), RoomMemberships::empty()).await?;
1029
1030        for user_id in user_ids {
1031            let devices = self.client.encryption().get_user_devices(&user_id).await?;
1032            let any_unverified = devices.devices().any(|d| !d.is_verified());
1033
1034            if any_unverified {
1035                return Ok(false);
1036            }
1037        }
1038
1039        Ok(true)
1040    }
1041
1042    /// Set the given account data event for this room.
1043    ///
1044    /// # Example
1045    /// ```
1046    /// # async {
1047    /// # let room: matrix_sdk::Room = todo!();
1048    /// # let event_id: ruma::OwnedEventId = todo!();
1049    /// use matrix_sdk::ruma::events::fully_read::FullyReadEventContent;
1050    /// let content = FullyReadEventContent::new(event_id);
1051    ///
1052    /// room.set_account_data(content).await?;
1053    /// # anyhow::Ok(())
1054    /// # };
1055    /// ```
1056    pub async fn set_account_data<T>(
1057        &self,
1058        content: T,
1059    ) -> Result<set_room_account_data::v3::Response>
1060    where
1061        T: RoomAccountDataEventContent,
1062    {
1063        let own_user = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
1064
1065        let request = set_room_account_data::v3::Request::new(
1066            own_user.to_owned(),
1067            self.room_id().to_owned(),
1068            &content,
1069        )?;
1070
1071        Ok(self.client.send(request).await?)
1072    }
1073
1074    /// Set the given raw account data event in this room.
1075    ///
1076    /// # Example
1077    /// ```
1078    /// # async {
1079    /// # let room: matrix_sdk::Room = todo!();
1080    /// use matrix_sdk::ruma::{
1081    ///     events::{
1082    ///         marked_unread::MarkedUnreadEventContent,
1083    ///         AnyRoomAccountDataEventContent, EventContent,
1084    ///     },
1085    ///     serde::Raw,
1086    /// };
1087    /// let marked_unread_content = MarkedUnreadEventContent::new(true);
1088    /// let full_event: AnyRoomAccountDataEventContent =
1089    ///     marked_unread_content.clone().into();
1090    /// room.set_account_data_raw(
1091    ///     marked_unread_content.event_type(),
1092    ///     Raw::new(&full_event).unwrap(),
1093    /// )
1094    /// .await?;
1095    /// # anyhow::Ok(())
1096    /// # };
1097    /// ```
1098    pub async fn set_account_data_raw(
1099        &self,
1100        event_type: RoomAccountDataEventType,
1101        content: Raw<AnyRoomAccountDataEventContent>,
1102    ) -> Result<set_room_account_data::v3::Response> {
1103        let own_user = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
1104
1105        let request = set_room_account_data::v3::Request::new_raw(
1106            own_user.to_owned(),
1107            self.room_id().to_owned(),
1108            event_type,
1109            content,
1110        );
1111
1112        Ok(self.client.send(request).await?)
1113    }
1114
1115    /// Adds a tag to the room, or updates it if it already exists.
1116    ///
1117    /// Returns the [`create_tag::v3::Response`] from the server.
1118    ///
1119    /// # Arguments
1120    /// * `tag` - The tag to add or update.
1121    ///
1122    /// * `tag_info` - Information about the tag, generally containing the
1123    ///   `order` parameter.
1124    ///
1125    /// # Examples
1126    ///
1127    /// ```no_run
1128    /// # use std::str::FromStr;
1129    /// # use ruma::events::tag::{TagInfo, TagName, UserTagName};
1130    /// # async {
1131    /// # let homeserver = url::Url::parse("http://localhost:8080")?;
1132    /// # let mut client = matrix_sdk::Client::new(homeserver).await?;
1133    /// # let room_id = matrix_sdk::ruma::room_id!("!test:localhost");
1134    /// use matrix_sdk::ruma::events::tag::TagInfo;
1135    ///
1136    /// if let Some(room) = client.get_room(&room_id) {
1137    ///     let mut tag_info = TagInfo::new();
1138    ///     tag_info.order = Some(0.9);
1139    ///     let user_tag = UserTagName::from_str("u.work")?;
1140    ///
1141    ///     room.set_tag(TagName::User(user_tag), tag_info).await?;
1142    /// }
1143    /// # anyhow::Ok(()) };
1144    /// ```
1145    pub async fn set_tag(
1146        &self,
1147        tag: TagName,
1148        tag_info: TagInfo,
1149    ) -> Result<create_tag::v3::Response> {
1150        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
1151        let request = create_tag::v3::Request::new(
1152            user_id.to_owned(),
1153            self.inner.room_id().to_owned(),
1154            tag.to_string(),
1155            tag_info,
1156        );
1157        Ok(self.client.send(request).await?)
1158    }
1159
1160    /// Removes a tag from the room.
1161    ///
1162    /// Returns the [`delete_tag::v3::Response`] from the server.
1163    ///
1164    /// # Arguments
1165    /// * `tag` - The tag to remove.
1166    pub async fn remove_tag(&self, tag: TagName) -> Result<delete_tag::v3::Response> {
1167        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
1168        let request = delete_tag::v3::Request::new(
1169            user_id.to_owned(),
1170            self.inner.room_id().to_owned(),
1171            tag.to_string(),
1172        );
1173        Ok(self.client.send(request).await?)
1174    }
1175
1176    /// Add or remove the `m.favourite` flag for this room.
1177    ///
1178    /// If `is_favourite` is `true`, and the `m.low_priority` tag is set on the
1179    /// room, the tag will be removed too.
1180    ///
1181    /// # Arguments
1182    ///
1183    /// * `is_favourite` - Whether to mark this room as favourite.
1184    /// * `tag_order` - The order of the tag if any.
1185    pub async fn set_is_favourite(&self, is_favourite: bool, tag_order: Option<f64>) -> Result<()> {
1186        if is_favourite {
1187            let tag_info = assign!(TagInfo::new(), { order: tag_order });
1188
1189            self.set_tag(TagName::Favorite, tag_info).await?;
1190
1191            if self.is_low_priority() {
1192                self.remove_tag(TagName::LowPriority).await?;
1193            }
1194        } else {
1195            self.remove_tag(TagName::Favorite).await?;
1196        }
1197        Ok(())
1198    }
1199
1200    /// Add or remove the `m.lowpriority` flag for this room.
1201    ///
1202    /// If `is_low_priority` is `true`, and the `m.favourite` tag is set on the
1203    /// room, the tag will be removed too.
1204    ///
1205    /// # Arguments
1206    ///
1207    /// * `is_low_priority` - Whether to mark this room as low_priority or not.
1208    /// * `tag_order` - The order of the tag if any.
1209    pub async fn set_is_low_priority(
1210        &self,
1211        is_low_priority: bool,
1212        tag_order: Option<f64>,
1213    ) -> Result<()> {
1214        if is_low_priority {
1215            let tag_info = assign!(TagInfo::new(), { order: tag_order });
1216
1217            self.set_tag(TagName::LowPriority, tag_info).await?;
1218
1219            if self.is_favourite() {
1220                self.remove_tag(TagName::Favorite).await?;
1221            }
1222        } else {
1223            self.remove_tag(TagName::LowPriority).await?;
1224        }
1225        Ok(())
1226    }
1227
1228    /// Sets whether this room is a DM.
1229    ///
1230    /// When setting this room as DM, it will be marked as DM for all active
1231    /// members of the room. When unsetting this room as DM, it will be
1232    /// unmarked as DM for all users, not just the members.
1233    ///
1234    /// # Arguments
1235    /// * `is_direct` - Whether to mark this room as direct.
1236    pub async fn set_is_direct(&self, is_direct: bool) -> Result<()> {
1237        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
1238
1239        let mut content = self
1240            .client
1241            .account()
1242            .account_data::<DirectEventContent>()
1243            .await?
1244            .map(|c| c.deserialize())
1245            .transpose()?
1246            .unwrap_or_default();
1247
1248        let this_room_id = self.inner.room_id();
1249
1250        if is_direct {
1251            let mut room_members = self.members(RoomMemberships::ACTIVE).await?;
1252            room_members.retain(|member| member.user_id() != self.own_user_id());
1253
1254            for member in room_members {
1255                let entry = content.entry(member.user_id().into()).or_default();
1256                if !entry.iter().any(|room_id| room_id == this_room_id) {
1257                    entry.push(this_room_id.to_owned());
1258                }
1259            }
1260        } else {
1261            for (_, list) in content.iter_mut() {
1262                list.retain(|room_id| *room_id != this_room_id);
1263            }
1264
1265            // Remove user ids that don't have any room marked as DM
1266            content.retain(|_, list| !list.is_empty());
1267        }
1268
1269        let request = set_global_account_data::v3::Request::new(user_id.to_owned(), &content)?;
1270
1271        self.client.send(request).await?;
1272        Ok(())
1273    }
1274
1275    /// Tries to decrypt a room event.
1276    ///
1277    /// # Arguments
1278    /// * `event` - The room event to be decrypted.
1279    ///
1280    /// Returns the decrypted event. In the case of a decryption error, returns
1281    /// a `TimelineEvent` representing the decryption error.
1282    #[cfg(feature = "e2e-encryption")]
1283    pub async fn decrypt_event(
1284        &self,
1285        event: &Raw<OriginalSyncRoomEncryptedEvent>,
1286    ) -> Result<TimelineEvent> {
1287        let machine = self.client.olm_machine().await;
1288        let machine = machine.as_ref().ok_or(Error::NoOlmMachine)?;
1289
1290        let decryption_settings = DecryptionSettings {
1291            sender_device_trust_requirement: self.client.base_client().decryption_trust_requirement,
1292        };
1293        let mut event: TimelineEvent = match machine
1294            .try_decrypt_room_event(event.cast_ref(), self.inner.room_id(), &decryption_settings)
1295            .await?
1296        {
1297            RoomEventDecryptionResult::Decrypted(decrypted) => decrypted.into(),
1298            RoomEventDecryptionResult::UnableToDecrypt(utd_info) => {
1299                self.client
1300                    .encryption()
1301                    .backups()
1302                    .maybe_download_room_key(self.room_id().to_owned(), event.clone());
1303                TimelineEvent::new_utd_event(event.clone().cast(), utd_info)
1304            }
1305        };
1306
1307        event.push_actions = self.event_push_actions(event.raw()).await?;
1308        Ok(event)
1309    }
1310
1311    /// Forces the currently active room key, which is used to encrypt messages,
1312    /// to be rotated.
1313    ///
1314    /// A new room key will be crated and shared with all the room members the
1315    /// next time a message will be sent. You don't have to call this method,
1316    /// room keys will be rotated automatically when necessary. This method is
1317    /// still useful for debugging purposes.
1318    ///
1319    /// For more info please take a look a the [`encryption`] module
1320    /// documentation.
1321    ///
1322    /// [`encryption`]: crate::encryption
1323    #[cfg(feature = "e2e-encryption")]
1324    pub async fn discard_room_key(&self) -> Result<()> {
1325        let machine = self.client.olm_machine().await;
1326        if let Some(machine) = machine.as_ref() {
1327            machine.discard_room_key(self.inner.room_id()).await?;
1328            Ok(())
1329        } else {
1330            Err(Error::NoOlmMachine)
1331        }
1332    }
1333
1334    /// Ban the user with `UserId` from this room.
1335    ///
1336    /// # Arguments
1337    ///
1338    /// * `user_id` - The user to ban with `UserId`.
1339    ///
1340    /// * `reason` - The reason for banning this user.
1341    #[instrument(skip_all)]
1342    pub async fn ban_user(&self, user_id: &UserId, reason: Option<&str>) -> Result<()> {
1343        let request = assign!(
1344            ban_user::v3::Request::new(self.room_id().to_owned(), user_id.to_owned()),
1345            { reason: reason.map(ToOwned::to_owned) }
1346        );
1347        self.client.send(request).await?;
1348        Ok(())
1349    }
1350
1351    /// Unban the user with `UserId` from this room.
1352    ///
1353    /// # Arguments
1354    ///
1355    /// * `user_id` - The user to unban with `UserId`.
1356    ///
1357    /// * `reason` - The reason for unbanning this user.
1358    #[instrument(skip_all)]
1359    pub async fn unban_user(&self, user_id: &UserId, reason: Option<&str>) -> Result<()> {
1360        let request = assign!(
1361            unban_user::v3::Request::new(self.room_id().to_owned(), user_id.to_owned()),
1362            { reason: reason.map(ToOwned::to_owned) }
1363        );
1364        self.client.send(request).await?;
1365        Ok(())
1366    }
1367
1368    /// Kick a user out of this room.
1369    ///
1370    /// # Arguments
1371    ///
1372    /// * `user_id` - The `UserId` of the user that should be kicked out of the
1373    ///   room.
1374    ///
1375    /// * `reason` - Optional reason why the room member is being kicked out.
1376    #[instrument(skip_all)]
1377    pub async fn kick_user(&self, user_id: &UserId, reason: Option<&str>) -> Result<()> {
1378        let request = assign!(
1379            kick_user::v3::Request::new(self.room_id().to_owned(), user_id.to_owned()),
1380            { reason: reason.map(ToOwned::to_owned) }
1381        );
1382        self.client.send(request).await?;
1383        Ok(())
1384    }
1385
1386    /// Invite the specified user by `UserId` to this room.
1387    ///
1388    /// # Arguments
1389    ///
1390    /// * `user_id` - The `UserId` of the user to invite to the room.
1391    #[instrument(skip_all)]
1392    pub async fn invite_user_by_id(&self, user_id: &UserId) -> Result<()> {
1393        let recipient = InvitationRecipient::UserId { user_id: user_id.to_owned() };
1394        let request = invite_user::v3::Request::new(self.room_id().to_owned(), recipient);
1395        self.client.send(request).await?;
1396
1397        // Force a future room members reload before sending any event to prevent UTDs
1398        // that can happen when some event is sent after a room member has been invited
1399        // but before the /sync request could fetch the membership change event.
1400        self.mark_members_missing();
1401
1402        Ok(())
1403    }
1404
1405    /// Invite the specified user by third party id to this room.
1406    ///
1407    /// # Arguments
1408    ///
1409    /// * `invite_id` - A third party id of a user to invite to the room.
1410    #[instrument(skip_all)]
1411    pub async fn invite_user_by_3pid(&self, invite_id: Invite3pid) -> Result<()> {
1412        let recipient = InvitationRecipient::ThirdPartyId(invite_id);
1413        let request = invite_user::v3::Request::new(self.room_id().to_owned(), recipient);
1414        self.client.send(request).await?;
1415
1416        // Force a future room members reload before sending any event to prevent UTDs
1417        // that can happen when some event is sent after a room member has been invited
1418        // but before the /sync request could fetch the membership change event.
1419        self.mark_members_missing();
1420
1421        Ok(())
1422    }
1423
1424    /// Activate typing notice for this room.
1425    ///
1426    /// The typing notice remains active for 4s. It can be deactivate at any
1427    /// point by setting typing to `false`. If this method is called while
1428    /// the typing notice is active nothing will happen. This method can be
1429    /// called on every key stroke, since it will do nothing while typing is
1430    /// active.
1431    ///
1432    /// # Arguments
1433    ///
1434    /// * `typing` - Whether the user is typing or has stopped typing.
1435    ///
1436    /// # Examples
1437    ///
1438    /// ```no_run
1439    /// use std::time::Duration;
1440    ///
1441    /// use matrix_sdk::ruma::api::client::typing::create_typing_event::v3::Typing;
1442    /// # use matrix_sdk::{
1443    /// #     Client, config::SyncSettings,
1444    /// #     ruma::room_id,
1445    /// # };
1446    /// # use url::Url;
1447    ///
1448    /// # async {
1449    /// # let homeserver = Url::parse("http://localhost:8080")?;
1450    /// # let client = Client::new(homeserver).await?;
1451    /// let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost");
1452    ///
1453    /// if let Some(room) = client.get_room(&room_id) {
1454    ///     room.typing_notice(true).await?
1455    /// }
1456    /// # anyhow::Ok(()) };
1457    /// ```
1458    pub async fn typing_notice(&self, typing: bool) -> Result<()> {
1459        self.ensure_room_joined()?;
1460
1461        // Only send a request to the homeserver if the old timeout has elapsed
1462        // or the typing notice changed state within the `TYPING_NOTICE_TIMEOUT`
1463        let send = if let Some(typing_time) =
1464            self.client.inner.typing_notice_times.read().unwrap().get(self.room_id())
1465        {
1466            if typing_time.elapsed() > TYPING_NOTICE_RESEND_TIMEOUT {
1467                // We always reactivate the typing notice if typing is true or
1468                // we may need to deactivate it if it's
1469                // currently active if typing is false
1470                typing || typing_time.elapsed() <= TYPING_NOTICE_TIMEOUT
1471            } else {
1472                // Only send a request when we need to deactivate typing
1473                !typing
1474            }
1475        } else {
1476            // Typing notice is currently deactivated, therefore, send a request
1477            // only when it's about to be activated
1478            typing
1479        };
1480
1481        if send {
1482            self.send_typing_notice(typing).await?;
1483        }
1484
1485        Ok(())
1486    }
1487
1488    #[instrument(name = "typing_notice", skip(self))]
1489    async fn send_typing_notice(&self, typing: bool) -> Result<()> {
1490        let typing = if typing {
1491            self.client
1492                .inner
1493                .typing_notice_times
1494                .write()
1495                .unwrap()
1496                .insert(self.room_id().to_owned(), Instant::now());
1497            Typing::Yes(TYPING_NOTICE_TIMEOUT)
1498        } else {
1499            self.client.inner.typing_notice_times.write().unwrap().remove(self.room_id());
1500            Typing::No
1501        };
1502
1503        let request = create_typing_event::v3::Request::new(
1504            self.own_user_id().to_owned(),
1505            self.room_id().to_owned(),
1506            typing,
1507        );
1508
1509        self.client.send(request).await?;
1510
1511        Ok(())
1512    }
1513
1514    /// Send a request to set a single receipt.
1515    ///
1516    /// # Arguments
1517    ///
1518    /// * `receipt_type` - The type of the receipt to set. Note that it is
1519    ///   possible to set the fully-read marker although it is technically not a
1520    ///   receipt.
1521    ///
1522    /// * `thread` - The thread where this receipt should apply, if any. Note
1523    ///   that this must be [`ReceiptThread::Unthreaded`] when sending a
1524    ///   [`ReceiptType::FullyRead`][create_receipt::v3::ReceiptType::FullyRead].
1525    ///
1526    /// * `event_id` - The `EventId` of the event to set the receipt on.
1527    #[instrument(skip_all)]
1528    pub async fn send_single_receipt(
1529        &self,
1530        receipt_type: create_receipt::v3::ReceiptType,
1531        thread: ReceiptThread,
1532        event_id: OwnedEventId,
1533    ) -> Result<()> {
1534        // Since the receipt type and the thread aren't Hash/Ord, flatten then as a
1535        // string key.
1536        let request_key = format!("{}|{}", receipt_type, thread.as_str().unwrap_or("<unthreaded>"));
1537
1538        self.client
1539            .inner
1540            .locks
1541            .read_receipt_deduplicated_handler
1542            .run((request_key, event_id.clone()), async {
1543                let mut request = create_receipt::v3::Request::new(
1544                    self.room_id().to_owned(),
1545                    receipt_type,
1546                    event_id,
1547                );
1548                request.thread = thread;
1549
1550                self.client.send(request).await?;
1551                Ok(())
1552            })
1553            .await
1554    }
1555
1556    /// Send a request to set multiple receipts at once.
1557    ///
1558    /// # Arguments
1559    ///
1560    /// * `receipts` - The `Receipts` to send.
1561    ///
1562    /// If `receipts` is empty, this is a no-op.
1563    #[instrument(skip_all)]
1564    pub async fn send_multiple_receipts(&self, receipts: Receipts) -> Result<()> {
1565        if receipts.is_empty() {
1566            return Ok(());
1567        }
1568
1569        let Receipts { fully_read, public_read_receipt, private_read_receipt } = receipts;
1570        let request = assign!(set_read_marker::v3::Request::new(self.room_id().to_owned()), {
1571            fully_read,
1572            read_receipt: public_read_receipt,
1573            private_read_receipt,
1574        });
1575
1576        self.client.send(request).await?;
1577        Ok(())
1578    }
1579
1580    /// Enable End-to-end encryption in this room.
1581    ///
1582    /// This method will be a noop if encryption is already enabled, otherwise
1583    /// sends a `m.room.encryption` state event to the room. This might fail if
1584    /// you don't have the appropriate power level to enable end-to-end
1585    /// encryption.
1586    ///
1587    /// A sync needs to be received to update the local room state. This method
1588    /// will wait for a sync to be received, this might time out if no
1589    /// sync loop is running or if the server is slow.
1590    ///
1591    /// # Examples
1592    ///
1593    /// ```no_run
1594    /// # use matrix_sdk::{
1595    /// #     Client, config::SyncSettings,
1596    /// #     ruma::room_id,
1597    /// # };
1598    /// # use url::Url;
1599    /// #
1600    /// # async {
1601    /// # let homeserver = Url::parse("http://localhost:8080")?;
1602    /// # let client = Client::new(homeserver).await?;
1603    /// # let room_id = room_id!("!test:localhost");
1604    /// let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost");
1605    ///
1606    /// if let Some(room) = client.get_room(&room_id) {
1607    ///     room.enable_encryption().await?
1608    /// }
1609    /// # anyhow::Ok(()) };
1610    /// ```
1611    #[instrument(skip_all)]
1612    pub async fn enable_encryption(&self) -> Result<()> {
1613        use ruma::{
1614            events::room::encryption::RoomEncryptionEventContent, EventEncryptionAlgorithm,
1615        };
1616        const SYNC_WAIT_TIME: Duration = Duration::from_secs(3);
1617
1618        if !self.is_encrypted().await? {
1619            let content =
1620                RoomEncryptionEventContent::new(EventEncryptionAlgorithm::MegolmV1AesSha2);
1621            self.send_state_event(content).await?;
1622
1623            // TODO do we want to return an error here if we time out? This
1624            // could be quite useful if someone wants to enable encryption and
1625            // send a message right after it's enabled.
1626            _ = timeout(self.client.inner.sync_beat.listen(), SYNC_WAIT_TIME).await;
1627
1628            // If after waiting for a sync, we don't have the encryption state we expect,
1629            // assume the local encryption state is incorrect; this will cause
1630            // the SDK to re-request it later for confirmation, instead of
1631            // assuming it's sync'd and correct (and not encrypted).
1632            let _sync_lock = self.client.base_client().sync_lock().lock().await;
1633            if !self.inner.is_encrypted() {
1634                debug!("still not marked as encrypted, marking encryption state as missing");
1635
1636                let mut room_info = self.clone_info();
1637                room_info.mark_encryption_state_missing();
1638                let mut changes = StateChanges::default();
1639                changes.add_room(room_info.clone());
1640
1641                self.client.store().save_changes(&changes).await?;
1642                self.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1643            } else {
1644                debug!("room successfully marked as encrypted");
1645            }
1646        }
1647
1648        Ok(())
1649    }
1650
1651    /// Share a room key with users in the given room.
1652    ///
1653    /// This will create Olm sessions with all the users/device pairs in the
1654    /// room if necessary and share a room key that can be shared with them.
1655    ///
1656    /// Does nothing if no room key needs to be shared.
1657    // TODO: expose this publicly so people can pre-share a group session if
1658    // e.g. a user starts to type a message for a room.
1659    #[cfg(feature = "e2e-encryption")]
1660    #[instrument(skip_all, fields(room_id = ?self.room_id(), store_generation))]
1661    async fn preshare_room_key(&self) -> Result<()> {
1662        self.ensure_room_joined()?;
1663
1664        // Take and release the lock on the store, if needs be.
1665        let guard = self.client.encryption().spin_lock_store(Some(60000)).await?;
1666        tracing::Span::current().record("store_generation", guard.map(|guard| guard.generation()));
1667
1668        self.client
1669            .locks()
1670            .group_session_deduplicated_handler
1671            .run(self.room_id().to_owned(), async move {
1672                {
1673                    let members = self
1674                        .client
1675                        .store()
1676                        .get_user_ids(self.room_id(), RoomMemberships::ACTIVE)
1677                        .await?;
1678                    self.client.claim_one_time_keys(members.iter().map(Deref::deref)).await?;
1679                };
1680
1681                let response = self.share_room_key().await;
1682
1683                // If one of the responses failed invalidate the group
1684                // session as using it would end up in undecryptable
1685                // messages.
1686                if let Err(r) = response {
1687                    let machine = self.client.olm_machine().await;
1688                    if let Some(machine) = machine.as_ref() {
1689                        machine.discard_room_key(self.room_id()).await?;
1690                    }
1691                    return Err(r);
1692                }
1693
1694                Ok(())
1695            })
1696            .await
1697    }
1698
1699    /// Share a group session for a room.
1700    ///
1701    /// # Panics
1702    ///
1703    /// Panics if the client isn't logged in.
1704    #[cfg(feature = "e2e-encryption")]
1705    #[instrument(skip_all)]
1706    async fn share_room_key(&self) -> Result<()> {
1707        self.ensure_room_joined()?;
1708
1709        let requests = self.client.base_client().share_room_key(self.room_id()).await?;
1710
1711        for request in requests {
1712            let response = self.client.send_to_device(&request).await?;
1713            self.client.mark_request_as_sent(&request.txn_id, &response).await?;
1714        }
1715
1716        Ok(())
1717    }
1718
1719    /// Wait for the room to be fully synced.
1720    ///
1721    /// This method makes sure the room that was returned when joining a room
1722    /// has been echoed back in the sync.
1723    ///
1724    /// Warning: This waits until a sync happens and does not return if no sync
1725    /// is happening. It can also return early when the room is not a joined
1726    /// room anymore.
1727    #[instrument(skip_all)]
1728    pub async fn sync_up(&self) {
1729        while !self.is_synced() && self.state() == RoomState::Joined {
1730            let wait_for_beat = self.client.inner.sync_beat.listen();
1731            // We don't care whether it's a timeout or a sync beat.
1732            let _ = timeout(wait_for_beat, Duration::from_millis(1000)).await;
1733        }
1734    }
1735
1736    /// Send a message-like event to this room.
1737    ///
1738    /// Returns the parsed response from the server.
1739    ///
1740    /// If the encryption feature is enabled this method will transparently
1741    /// encrypt the event if this room is encrypted (except for `m.reaction`
1742    /// events, which are never encrypted).
1743    ///
1744    /// **Note**: If you just want to send an event with custom JSON content to
1745    /// a room, you can use the [`send_raw()`][Self::send_raw] method for that.
1746    ///
1747    /// If you want to set a transaction ID for the event, use
1748    /// [`.with_transaction_id()`][SendMessageLikeEvent::with_transaction_id]
1749    /// on the returned value before `.await`ing it.
1750    ///
1751    /// # Arguments
1752    ///
1753    /// * `content` - The content of the message event.
1754    ///
1755    /// # Examples
1756    ///
1757    /// ```no_run
1758    /// # use std::sync::{Arc, RwLock};
1759    /// # use matrix_sdk::{Client, config::SyncSettings};
1760    /// # use url::Url;
1761    /// # use matrix_sdk::ruma::room_id;
1762    /// # use serde::{Deserialize, Serialize};
1763    /// use matrix_sdk::ruma::{
1764    ///     events::{
1765    ///         macros::EventContent,
1766    ///         room::message::{RoomMessageEventContent, TextMessageEventContent},
1767    ///     },
1768    ///     uint, MilliSecondsSinceUnixEpoch, TransactionId,
1769    /// };
1770    ///
1771    /// # async {
1772    /// # let homeserver = Url::parse("http://localhost:8080")?;
1773    /// # let mut client = Client::new(homeserver).await?;
1774    /// # let room_id = room_id!("!test:localhost");
1775    /// let content = RoomMessageEventContent::text_plain("Hello world");
1776    /// let txn_id = TransactionId::new();
1777    ///
1778    /// if let Some(room) = client.get_room(&room_id) {
1779    ///     room.send(content).with_transaction_id(txn_id).await?;
1780    /// }
1781    ///
1782    /// // Custom events work too:
1783    /// #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
1784    /// #[ruma_event(type = "org.shiny_new_2fa.token", kind = MessageLike)]
1785    /// struct TokenEventContent {
1786    ///     token: String,
1787    ///     #[serde(rename = "exp")]
1788    ///     expires_at: MilliSecondsSinceUnixEpoch,
1789    /// }
1790    ///
1791    /// # fn generate_token() -> String { todo!() }
1792    /// let content = TokenEventContent {
1793    ///     token: generate_token(),
1794    ///     expires_at: {
1795    ///         let now = MilliSecondsSinceUnixEpoch::now();
1796    ///         MilliSecondsSinceUnixEpoch(now.0 + uint!(30_000))
1797    ///     },
1798    /// };
1799    ///
1800    /// if let Some(room) = client.get_room(&room_id) {
1801    ///     room.send(content).await?;
1802    /// }
1803    /// # anyhow::Ok(()) };
1804    /// ```
1805    pub fn send(&self, content: impl MessageLikeEventContent) -> SendMessageLikeEvent<'_> {
1806        SendMessageLikeEvent::new(self, content)
1807    }
1808
1809    /// Run /keys/query requests for all the non-tracked users.
1810    #[cfg(feature = "e2e-encryption")]
1811    async fn query_keys_for_untracked_users(&self) -> Result<()> {
1812        let olm = self.client.olm_machine().await;
1813        let olm = olm.as_ref().expect("Olm machine wasn't started");
1814
1815        let members =
1816            self.client.store().get_user_ids(self.room_id(), RoomMemberships::ACTIVE).await?;
1817
1818        let tracked: HashMap<_, _> = olm
1819            .store()
1820            .load_tracked_users()
1821            .await?
1822            .into_iter()
1823            .map(|tracked| (tracked.user_id, tracked.dirty))
1824            .collect();
1825
1826        // A member has no unknown devices iff it was tracked *and* the tracking is
1827        // not considered dirty.
1828        let members_with_unknown_devices =
1829            members.iter().filter(|member| tracked.get(*member).is_none_or(|dirty| *dirty));
1830
1831        let (req_id, request) =
1832            olm.query_keys_for_users(members_with_unknown_devices.map(|owned| owned.borrow()));
1833
1834        if !request.device_keys.is_empty() {
1835            self.client.keys_query(&req_id, request.device_keys).await?;
1836        }
1837
1838        Ok(())
1839    }
1840
1841    /// Send a message-like event with custom JSON content to this room.
1842    ///
1843    /// Returns the parsed response from the server.
1844    ///
1845    /// If the encryption feature is enabled this method will transparently
1846    /// encrypt the event if this room is encrypted (except for `m.reaction`
1847    /// events, which are never encrypted).
1848    ///
1849    /// This method is equivalent to the [`send()`][Self::send] method but
1850    /// allows sending custom JSON payloads, e.g. constructed using the
1851    /// [`serde_json::json!()`] macro.
1852    ///
1853    /// If you want to set a transaction ID for the event, use
1854    /// [`.with_transaction_id()`][SendRawMessageLikeEvent::with_transaction_id]
1855    /// on the returned value before `.await`ing it.
1856    ///
1857    /// # Arguments
1858    ///
1859    /// * `event_type` - The type of the event.
1860    ///
1861    /// * `content` - The content of the event as a raw JSON value. The argument
1862    ///   type can be `serde_json::Value`, but also other raw JSON types; for
1863    ///   the full list check the documentation of
1864    ///   [`IntoRawMessageLikeEventContent`].
1865    ///
1866    /// # Examples
1867    ///
1868    /// ```no_run
1869    /// # use std::sync::{Arc, RwLock};
1870    /// # use matrix_sdk::{Client, config::SyncSettings};
1871    /// # use url::Url;
1872    /// # use matrix_sdk::ruma::room_id;
1873    /// # async {
1874    /// # let homeserver = Url::parse("http://localhost:8080")?;
1875    /// # let mut client = Client::new(homeserver).await?;
1876    /// # let room_id = room_id!("!test:localhost");
1877    /// use serde_json::json;
1878    ///
1879    /// if let Some(room) = client.get_room(&room_id) {
1880    ///     room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1881    /// }
1882    /// # anyhow::Ok(()) };
1883    /// ```
1884    #[instrument(skip_all, fields(event_type, room_id = ?self.room_id(), transaction_id, is_room_encrypted, event_id))]
1885    pub fn send_raw<'a>(
1886        &'a self,
1887        event_type: &'a str,
1888        content: impl IntoRawMessageLikeEventContent,
1889    ) -> SendRawMessageLikeEvent<'a> {
1890        // Note: the recorded instrument fields are saved in
1891        // `SendRawMessageLikeEvent::into_future`.
1892        SendRawMessageLikeEvent::new(self, event_type, content)
1893    }
1894
1895    /// Send an attachment to this room.
1896    ///
1897    /// This will upload the given data that the reader produces using the
1898    /// [`upload()`] method and post an event to the given room.
1899    /// If the room is encrypted and the encryption feature is enabled the
1900    /// upload will be encrypted.
1901    ///
1902    /// This is a convenience method that calls the
1903    /// [`upload()`] and afterwards the [`send()`].
1904    ///
1905    /// # Arguments
1906    /// * `filename` - The file name.
1907    ///
1908    /// * `content_type` - The type of the media, this will be used as the
1909    /// content-type header.
1910    ///
1911    /// * `reader` - A `Reader` that will be used to fetch the raw bytes of the
1912    /// media.
1913    ///
1914    /// * `config` - Metadata and configuration for the attachment.
1915    ///
1916    /// # Examples
1917    ///
1918    /// ```no_run
1919    /// # use std::fs;
1920    /// # use matrix_sdk::{Client, ruma::room_id, attachment::AttachmentConfig};
1921    /// # use url::Url;
1922    /// # use mime;
1923    /// # async {
1924    /// # let homeserver = Url::parse("http://localhost:8080")?;
1925    /// # let mut client = Client::new(homeserver).await?;
1926    /// # let room_id = room_id!("!test:localhost");
1927    /// let mut image = fs::read("/home/example/my-cat.jpg")?;
1928    ///
1929    /// if let Some(room) = client.get_room(&room_id) {
1930    ///     room.send_attachment(
1931    ///         "my_favorite_cat.jpg",
1932    ///         &mime::IMAGE_JPEG,
1933    ///         image,
1934    ///         AttachmentConfig::new(),
1935    ///     ).await?;
1936    /// }
1937    /// # anyhow::Ok(()) };
1938    /// ```
1939    ///
1940    /// [`upload()`]: crate::Media::upload
1941    /// [`send()`]: Self::send
1942    #[instrument(skip_all)]
1943    pub fn send_attachment<'a>(
1944        &'a self,
1945        filename: impl Into<String>,
1946        content_type: &'a Mime,
1947        data: Vec<u8>,
1948        config: AttachmentConfig,
1949    ) -> SendAttachment<'a> {
1950        SendAttachment::new(self, filename.into(), content_type, data, config)
1951    }
1952
1953    /// Prepare and send an attachment to this room.
1954    ///
1955    /// This will upload the given data that the reader produces using the
1956    /// [`upload()`](#method.upload) method and post an event to the given room.
1957    /// If the room is encrypted and the encryption feature is enabled the
1958    /// upload will be encrypted.
1959    ///
1960    /// This is a convenience method that calls the
1961    /// [`Client::upload()`](#Client::method.upload) and afterwards the
1962    /// [`send()`](#method.send).
1963    ///
1964    /// # Arguments
1965    /// * `filename` - The file name.
1966    ///
1967    /// * `content_type` - The type of the media, this will be used as the
1968    ///   content-type header.
1969    ///
1970    /// * `reader` - A `Reader` that will be used to fetch the raw bytes of the
1971    ///   media.
1972    ///
1973    /// * `config` - Metadata and configuration for the attachment.
1974    ///
1975    /// * `send_progress` - An observable to transmit forward progress about the
1976    ///   upload.
1977    ///
1978    /// * `store_in_cache` - A boolean defining whether the uploaded media will
1979    ///   be stored in the cache immediately after a successful upload.
1980    #[instrument(skip_all)]
1981    pub(super) async fn prepare_and_send_attachment<'a>(
1982        &'a self,
1983        filename: String,
1984        content_type: &'a Mime,
1985        data: Vec<u8>,
1986        mut config: AttachmentConfig,
1987        send_progress: SharedObservable<TransmissionProgress>,
1988        store_in_cache: bool,
1989    ) -> Result<send_message_event::v3::Response> {
1990        self.ensure_room_joined()?;
1991
1992        let txn_id = config.txn_id.take();
1993        let mentions = config.mentions.take();
1994
1995        let thumbnail = config.thumbnail.take();
1996
1997        // If necessary, store caching data for the thumbnail ahead of time.
1998        let thumbnail_cache_info = if store_in_cache {
1999            thumbnail
2000                .as_ref()
2001                .map(|thumbnail| (thumbnail.data.clone(), thumbnail.height, thumbnail.width))
2002        } else {
2003            None
2004        };
2005
2006        #[cfg(feature = "e2e-encryption")]
2007        let (media_source, thumbnail) = if self.is_encrypted().await? {
2008            self.client
2009                .upload_encrypted_media_and_thumbnail(content_type, &data, thumbnail, send_progress)
2010                .await?
2011        } else {
2012            self.client
2013                .media()
2014                .upload_plain_media_and_thumbnail(
2015                    content_type,
2016                    // TODO: get rid of this clone; wait for Ruma to use `Bytes` or something
2017                    // similar.
2018                    data.clone(),
2019                    thumbnail,
2020                    send_progress,
2021                )
2022                .await?
2023        };
2024
2025        #[cfg(not(feature = "e2e-encryption"))]
2026        let (media_source, thumbnail) = self
2027            .client
2028            .media()
2029            .upload_plain_media_and_thumbnail(content_type, data.clone(), thumbnail, send_progress)
2030            .await?;
2031
2032        if store_in_cache {
2033            let cache_store_lock_guard = self.client.event_cache_store().lock().await?;
2034
2035            // A failure to cache shouldn't prevent the whole upload from finishing
2036            // properly, so only log errors during caching.
2037
2038            debug!("caching the media");
2039            let request =
2040                MediaRequestParameters { source: media_source.clone(), format: MediaFormat::File };
2041
2042            if let Err(err) = cache_store_lock_guard
2043                .add_media_content(&request, data, IgnoreMediaRetentionPolicy::No)
2044                .await
2045            {
2046                warn!("unable to cache the media after uploading it: {err}");
2047            }
2048
2049            if let Some(((data, height, width), source)) =
2050                thumbnail_cache_info.zip(thumbnail.as_ref().map(|tuple| &tuple.0))
2051            {
2052                debug!("caching the thumbnail");
2053
2054                let request = MediaRequestParameters {
2055                    source: source.clone(),
2056                    format: MediaFormat::Thumbnail(MediaThumbnailSettings::new(width, height)),
2057                };
2058
2059                if let Err(err) = cache_store_lock_guard
2060                    .add_media_content(&request, data, IgnoreMediaRetentionPolicy::No)
2061                    .await
2062                {
2063                    warn!("unable to cache the media after uploading it: {err}");
2064                }
2065            }
2066        }
2067
2068        let content = Self::make_attachment_event(
2069            self.make_attachment_type(
2070                content_type,
2071                filename,
2072                media_source,
2073                config.caption,
2074                config.formatted_caption,
2075                config.info,
2076                thumbnail,
2077            ),
2078            mentions,
2079        );
2080
2081        let mut fut = self.send(content);
2082        if let Some(txn_id) = txn_id {
2083            fut = fut.with_transaction_id(txn_id);
2084        }
2085        fut.await
2086    }
2087
2088    /// Creates the inner [`MessageType`] for an already-uploaded media file
2089    /// provided by its source.
2090    #[allow(clippy::too_many_arguments)]
2091    pub(crate) fn make_attachment_type(
2092        &self,
2093        content_type: &Mime,
2094        filename: String,
2095        source: MediaSource,
2096        caption: Option<String>,
2097        formatted_caption: Option<FormattedBody>,
2098        info: Option<AttachmentInfo>,
2099        thumbnail: Option<(MediaSource, Box<ThumbnailInfo>)>,
2100    ) -> MessageType {
2101        // If caption is set, use it as body, and filename as the file name; otherwise,
2102        // body is the filename, and the filename is not set.
2103        // https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/2530-body-as-caption.md
2104        let (body, filename) = match caption {
2105            Some(caption) => (caption, Some(filename)),
2106            None => (filename, None),
2107        };
2108
2109        let (thumbnail_source, thumbnail_info) = thumbnail.unzip();
2110
2111        match content_type.type_() {
2112            mime::IMAGE => {
2113                let info = assign!(info.map(ImageInfo::from).unwrap_or_default(), {
2114                    mimetype: Some(content_type.as_ref().to_owned()),
2115                    thumbnail_source,
2116                    thumbnail_info
2117                });
2118                let content = assign!(ImageMessageEventContent::new(body, source), {
2119                    info: Some(Box::new(info)),
2120                    formatted: formatted_caption,
2121                    filename
2122                });
2123                MessageType::Image(content)
2124            }
2125
2126            mime::AUDIO => {
2127                let mut content = assign!(AudioMessageEventContent::new(body, source), {
2128                    formatted: formatted_caption,
2129                    filename
2130                });
2131
2132                if let Some(AttachmentInfo::Voice { audio_info, waveform: Some(waveform_vec) }) =
2133                    &info
2134                {
2135                    if let Some(duration) = audio_info.duration {
2136                        let waveform = waveform_vec.iter().map(|v| (*v).into()).collect();
2137                        content.audio =
2138                            Some(UnstableAudioDetailsContentBlock::new(duration, waveform));
2139                    }
2140                    content.voice = Some(UnstableVoiceContentBlock::new());
2141                }
2142
2143                let mut audio_info = info.map(AudioInfo::from).unwrap_or_default();
2144                audio_info.mimetype = Some(content_type.as_ref().to_owned());
2145                let content = content.info(Box::new(audio_info));
2146
2147                MessageType::Audio(content)
2148            }
2149
2150            mime::VIDEO => {
2151                let info = assign!(info.map(VideoInfo::from).unwrap_or_default(), {
2152                    mimetype: Some(content_type.as_ref().to_owned()),
2153                    thumbnail_source,
2154                    thumbnail_info
2155                });
2156                let content = assign!(VideoMessageEventContent::new(body, source), {
2157                    info: Some(Box::new(info)),
2158                    formatted: formatted_caption,
2159                    filename
2160                });
2161                MessageType::Video(content)
2162            }
2163
2164            _ => {
2165                let info = assign!(info.map(FileInfo::from).unwrap_or_default(), {
2166                    mimetype: Some(content_type.as_ref().to_owned()),
2167                    thumbnail_source,
2168                    thumbnail_info
2169                });
2170                let content = assign!(FileMessageEventContent::new(body, source), {
2171                    info: Some(Box::new(info)),
2172                    formatted: formatted_caption,
2173                    filename,
2174                });
2175                MessageType::File(content)
2176            }
2177        }
2178    }
2179
2180    /// Creates the [`RoomMessageEventContent`] based on the message type and
2181    /// mentions.
2182    pub(crate) fn make_attachment_event(
2183        msg_type: MessageType,
2184        mentions: Option<Mentions>,
2185    ) -> RoomMessageEventContent {
2186        let mut content = RoomMessageEventContent::new(msg_type);
2187        if let Some(mentions) = mentions {
2188            content = content.add_mentions(mentions);
2189        }
2190        content
2191    }
2192
2193    /// Update the power levels of a select set of users of this room.
2194    ///
2195    /// Issue a `power_levels` state event request to the server, changing the
2196    /// given UserId -> Int levels. May fail if the `power_levels` aren't
2197    /// locally known yet or the server rejects the state event update, e.g.
2198    /// because of insufficient permissions. Neither permissions to update
2199    /// nor whether the data might be stale is checked prior to issuing the
2200    /// request.
2201    pub async fn update_power_levels(
2202        &self,
2203        updates: Vec<(&UserId, Int)>,
2204    ) -> Result<send_state_event::v3::Response> {
2205        let mut power_levels = self.power_levels().await?;
2206
2207        for (user_id, new_level) in updates {
2208            if new_level == power_levels.users_default {
2209                power_levels.users.remove(user_id);
2210            } else {
2211                power_levels.users.insert(user_id.to_owned(), new_level);
2212            }
2213        }
2214
2215        self.send_state_event(RoomPowerLevelsEventContent::from(power_levels)).await
2216    }
2217
2218    /// Applies a set of power level changes to this room.
2219    ///
2220    /// Any values that are `None` in the given `RoomPowerLevelChanges` will
2221    /// remain unchanged.
2222    pub async fn apply_power_level_changes(&self, changes: RoomPowerLevelChanges) -> Result<()> {
2223        let mut power_levels = self.power_levels().await?;
2224        power_levels.apply(changes)?;
2225        self.send_state_event(RoomPowerLevelsEventContent::from(power_levels)).await?;
2226        Ok(())
2227    }
2228
2229    /// Resets the room's power levels to the default values
2230    ///
2231    /// [spec]: https://spec.matrix.org/v1.9/client-server-api/#mroompower_levels
2232    pub async fn reset_power_levels(&self) -> Result<RoomPowerLevels> {
2233        let default_power_levels = RoomPowerLevels::from(RoomPowerLevelsEventContent::new());
2234        let changes = RoomPowerLevelChanges::from(default_power_levels);
2235        self.apply_power_level_changes(changes).await?;
2236        Ok(self.power_levels().await?)
2237    }
2238
2239    /// Gets the suggested role for the user with the provided `user_id`.
2240    ///
2241    /// This method checks the `RoomPowerLevels` events instead of loading the
2242    /// member list and looking for the member.
2243    pub async fn get_suggested_user_role(&self, user_id: &UserId) -> Result<RoomMemberRole> {
2244        let power_level = self.get_user_power_level(user_id).await?;
2245        Ok(RoomMemberRole::suggested_role_for_power_level(power_level))
2246    }
2247
2248    /// Gets the power level the user with the provided `user_id`.
2249    ///
2250    /// This method checks the `RoomPowerLevels` events instead of loading the
2251    /// member list and looking for the member.
2252    pub async fn get_user_power_level(&self, user_id: &UserId) -> Result<i64> {
2253        let event = self.power_levels().await?;
2254        Ok(event.for_user(user_id).into())
2255    }
2256
2257    /// Gets a map with the `UserId` of users with power levels other than `0`
2258    /// and this power level.
2259    pub async fn users_with_power_levels(&self) -> HashMap<OwnedUserId, i64> {
2260        let power_levels = self.power_levels().await.ok();
2261        let mut user_power_levels = HashMap::<OwnedUserId, i64>::new();
2262        if let Some(power_levels) = power_levels {
2263            for (id, level) in power_levels.users.into_iter() {
2264                user_power_levels.insert(id, level.into());
2265            }
2266        }
2267        user_power_levels
2268    }
2269
2270    /// Sets the name of this room.
2271    pub async fn set_name(&self, name: String) -> Result<send_state_event::v3::Response> {
2272        self.send_state_event(RoomNameEventContent::new(name)).await
2273    }
2274
2275    /// Sets a new topic for this room.
2276    pub async fn set_room_topic(&self, topic: &str) -> Result<send_state_event::v3::Response> {
2277        self.send_state_event(RoomTopicEventContent::new(topic.into())).await
2278    }
2279
2280    /// Sets the new avatar url for this room.
2281    ///
2282    /// # Arguments
2283    /// * `avatar_url` - The owned matrix uri that represents the avatar
2284    /// * `info` - The optional image info that can be provided for the avatar
2285    pub async fn set_avatar_url(
2286        &self,
2287        url: &MxcUri,
2288        info: Option<avatar::ImageInfo>,
2289    ) -> Result<send_state_event::v3::Response> {
2290        self.ensure_room_joined()?;
2291
2292        let mut room_avatar_event = RoomAvatarEventContent::new();
2293        room_avatar_event.url = Some(url.to_owned());
2294        room_avatar_event.info = info.map(Box::new);
2295
2296        self.send_state_event(room_avatar_event).await
2297    }
2298
2299    /// Removes the avatar from the room
2300    pub async fn remove_avatar(&self) -> Result<send_state_event::v3::Response> {
2301        self.send_state_event(RoomAvatarEventContent::new()).await
2302    }
2303
2304    /// Uploads a new avatar for this room.
2305    ///
2306    /// # Arguments
2307    /// * `mime` - The mime type describing the data
2308    /// * `data` - The data representation of the avatar
2309    /// * `info` - The optional image info provided for the avatar, the blurhash
2310    ///   and the mimetype will always be updated
2311    pub async fn upload_avatar(
2312        &self,
2313        mime: &Mime,
2314        data: Vec<u8>,
2315        info: Option<avatar::ImageInfo>,
2316    ) -> Result<send_state_event::v3::Response> {
2317        self.ensure_room_joined()?;
2318
2319        let upload_response = self.client.media().upload(mime, data, None).await?;
2320        let mut info = info.unwrap_or_default();
2321        info.blurhash = upload_response.blurhash;
2322        info.mimetype = Some(mime.to_string());
2323
2324        self.set_avatar_url(&upload_response.content_uri, Some(info)).await
2325    }
2326
2327    /// Send a state event with an empty state key to the homeserver.
2328    ///
2329    /// For state events with a non-empty state key, see
2330    /// [`send_state_event_for_key`][Self::send_state_event_for_key].
2331    ///
2332    /// Returns the parsed response from the server.
2333    ///
2334    /// # Arguments
2335    ///
2336    /// * `content` - The content of the state event.
2337    ///
2338    /// # Examples
2339    ///
2340    /// ```no_run
2341    /// # use serde::{Deserialize, Serialize};
2342    /// # async {
2343    /// # let joined_room: matrix_sdk::Room = todo!();
2344    /// use matrix_sdk::ruma::{
2345    ///     events::{
2346    ///         macros::EventContent, room::encryption::RoomEncryptionEventContent,
2347    ///         EmptyStateKey,
2348    ///     },
2349    ///     EventEncryptionAlgorithm,
2350    /// };
2351    ///
2352    /// let encryption_event_content = RoomEncryptionEventContent::new(
2353    ///     EventEncryptionAlgorithm::MegolmV1AesSha2,
2354    /// );
2355    /// joined_room.send_state_event(encryption_event_content).await?;
2356    ///
2357    /// // Custom event:
2358    /// #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
2359    /// #[ruma_event(
2360    ///     type = "org.matrix.msc_9000.xxx",
2361    ///     kind = State,
2362    ///     state_key_type = EmptyStateKey,
2363    /// )]
2364    /// struct XxxStateEventContent {/* fields... */}
2365    ///
2366    /// let content: XxxStateEventContent = todo!();
2367    /// joined_room.send_state_event(content).await?;
2368    /// # anyhow::Ok(()) };
2369    /// ```
2370    #[instrument(skip_all)]
2371    pub async fn send_state_event(
2372        &self,
2373        content: impl StateEventContent<StateKey = EmptyStateKey>,
2374    ) -> Result<send_state_event::v3::Response> {
2375        self.send_state_event_for_key(&EmptyStateKey, content).await
2376    }
2377
2378    /// Send a state event to the homeserver.
2379    ///
2380    /// Returns the parsed response from the server.
2381    ///
2382    /// # Arguments
2383    ///
2384    /// * `content` - The content of the state event.
2385    ///
2386    /// * `state_key` - A unique key which defines the overwriting semantics for
2387    ///   this piece of room state.
2388    ///
2389    /// # Examples
2390    ///
2391    /// ```no_run
2392    /// # use serde::{Deserialize, Serialize};
2393    /// # async {
2394    /// # let joined_room: matrix_sdk::Room = todo!();
2395    /// use matrix_sdk::ruma::{
2396    ///     events::{
2397    ///         macros::EventContent,
2398    ///         room::member::{RoomMemberEventContent, MembershipState},
2399    ///     },
2400    ///     mxc_uri,
2401    /// };
2402    ///
2403    /// let avatar_url = mxc_uri!("mxc://example.org/avatar").to_owned();
2404    /// let mut content = RoomMemberEventContent::new(MembershipState::Join);
2405    /// content.avatar_url = Some(avatar_url);
2406    ///
2407    /// joined_room.send_state_event_for_key(ruma::user_id!("@foo:bar.com"), content).await?;
2408    ///
2409    /// // Custom event:
2410    /// #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
2411    /// #[ruma_event(type = "org.matrix.msc_9000.xxx", kind = State, state_key_type = String)]
2412    /// struct XxxStateEventContent { /* fields... */ }
2413    ///
2414    /// let content: XxxStateEventContent = todo!();
2415    /// joined_room.send_state_event_for_key("foo", content).await?;
2416    /// # anyhow::Ok(()) };
2417    /// ```
2418    pub async fn send_state_event_for_key<C, K>(
2419        &self,
2420        state_key: &K,
2421        content: C,
2422    ) -> Result<send_state_event::v3::Response>
2423    where
2424        C: StateEventContent,
2425        C::StateKey: Borrow<K>,
2426        K: AsRef<str> + ?Sized,
2427    {
2428        self.ensure_room_joined()?;
2429        let request =
2430            send_state_event::v3::Request::new(self.room_id().to_owned(), state_key, &content)?;
2431        let response = self.client.send(request).await?;
2432        Ok(response)
2433    }
2434
2435    /// Send a raw room state event to the homeserver.
2436    ///
2437    /// Returns the parsed response from the server.
2438    ///
2439    /// # Arguments
2440    ///
2441    /// * `event_type` - The type of the event that we're sending out.
2442    ///
2443    /// * `state_key` - A unique key which defines the overwriting semantics for
2444    /// this piece of room state. This value is often a zero-length string.
2445    ///
2446    /// * `content` - The content of the event as a raw JSON value. The argument
2447    ///   type can be `serde_json::Value`, but also other raw JSON types; for
2448    ///   the full list check the documentation of [`IntoRawStateEventContent`].
2449    ///
2450    /// # Examples
2451    ///
2452    /// ```no_run
2453    /// use serde_json::json;
2454    ///
2455    /// # async {
2456    /// # let homeserver = url::Url::parse("http://localhost:8080")?;
2457    /// # let mut client = matrix_sdk::Client::new(homeserver).await?;
2458    /// # let room_id = matrix_sdk::ruma::room_id!("!test:localhost");
2459    ///
2460    /// if let Some(room) = client.get_room(&room_id) {
2461    ///     room.send_state_event_raw("m.room.member", "", json!({
2462    ///         "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
2463    ///         "displayname": "Alice Margatroid",
2464    ///         "membership": "join",
2465    ///     })).await?;
2466    /// }
2467    /// # anyhow::Ok(()) };
2468    /// ```
2469    #[instrument(skip_all)]
2470    pub async fn send_state_event_raw(
2471        &self,
2472        event_type: &str,
2473        state_key: &str,
2474        content: impl IntoRawStateEventContent,
2475    ) -> Result<send_state_event::v3::Response> {
2476        self.ensure_room_joined()?;
2477
2478        let request = send_state_event::v3::Request::new_raw(
2479            self.room_id().to_owned(),
2480            event_type.into(),
2481            state_key.to_owned(),
2482            content.into_raw_state_event_content(),
2483        );
2484
2485        Ok(self.client.send(request).await?)
2486    }
2487
2488    /// Strips all information out of an event of the room.
2489    ///
2490    /// Returns the [`redact_event::v3::Response`] from the server.
2491    ///
2492    /// This cannot be undone. Users may redact their own events, and any user
2493    /// with a power level greater than or equal to the redact power level of
2494    /// the room may redact events there.
2495    ///
2496    /// # Arguments
2497    ///
2498    /// * `event_id` - The ID of the event to redact
2499    ///
2500    /// * `reason` - The reason for the event being redacted.
2501    ///
2502    /// * `txn_id` - A unique ID that can be attached to this event as
2503    /// its transaction ID. If not given one is created for the message.
2504    ///
2505    /// # Examples
2506    ///
2507    /// ```no_run
2508    /// use matrix_sdk::ruma::event_id;
2509    ///
2510    /// # async {
2511    /// # let homeserver = url::Url::parse("http://localhost:8080")?;
2512    /// # let mut client = matrix_sdk::Client::new(homeserver).await?;
2513    /// # let room_id = matrix_sdk::ruma::room_id!("!test:localhost");
2514    /// #
2515    /// if let Some(room) = client.get_room(&room_id) {
2516    ///     let event_id = event_id!("$xxxxxx:example.org");
2517    ///     let reason = Some("Indecent material");
2518    ///     room.redact(&event_id, reason, None).await?;
2519    /// }
2520    /// # anyhow::Ok(()) };
2521    /// ```
2522    #[instrument(skip_all)]
2523    pub async fn redact(
2524        &self,
2525        event_id: &EventId,
2526        reason: Option<&str>,
2527        txn_id: Option<OwnedTransactionId>,
2528    ) -> HttpResult<redact_event::v3::Response> {
2529        let txn_id = txn_id.unwrap_or_else(TransactionId::new);
2530        let request = assign!(
2531            redact_event::v3::Request::new(self.room_id().to_owned(), event_id.to_owned(), txn_id),
2532            { reason: reason.map(ToOwned::to_owned) }
2533        );
2534
2535        self.client.send(request).await
2536    }
2537
2538    /// Returns true if the user with the given user_id is able to redact
2539    /// their own messages in the room.
2540    ///
2541    /// The call may fail if there is an error in getting the power levels.
2542    pub async fn can_user_redact_own(&self, user_id: &UserId) -> Result<bool> {
2543        Ok(self.power_levels().await?.user_can_redact_own_event(user_id))
2544    }
2545
2546    /// Returns true if the user with the given user_id is able to redact
2547    /// messages of other users in the room.
2548    ///
2549    /// The call may fail if there is an error in getting the power levels.
2550    pub async fn can_user_redact_other(&self, user_id: &UserId) -> Result<bool> {
2551        Ok(self.power_levels().await?.user_can_redact_event_of_other(user_id))
2552    }
2553
2554    /// Returns true if the user with the given user_id is able to ban in the
2555    /// room.
2556    ///
2557    /// The call may fail if there is an error in getting the power levels.
2558    pub async fn can_user_ban(&self, user_id: &UserId) -> Result<bool> {
2559        Ok(self.power_levels().await?.user_can_ban(user_id))
2560    }
2561
2562    /// Returns true if the user with the given user_id is able to kick in the
2563    /// room.
2564    ///
2565    /// The call may fail if there is an error in getting the power levels.
2566    pub async fn can_user_invite(&self, user_id: &UserId) -> Result<bool> {
2567        Ok(self.power_levels().await?.user_can_invite(user_id))
2568    }
2569
2570    /// Returns true if the user with the given user_id is able to kick in the
2571    /// room.
2572    ///
2573    /// The call may fail if there is an error in getting the power levels.
2574    pub async fn can_user_kick(&self, user_id: &UserId) -> Result<bool> {
2575        Ok(self.power_levels().await?.user_can_kick(user_id))
2576    }
2577
2578    /// Returns true if the user with the given user_id is able to send a
2579    /// specific state event type in the room.
2580    ///
2581    /// The call may fail if there is an error in getting the power levels.
2582    pub async fn can_user_send_state(
2583        &self,
2584        user_id: &UserId,
2585        state_event: StateEventType,
2586    ) -> Result<bool> {
2587        Ok(self.power_levels().await?.user_can_send_state(user_id, state_event))
2588    }
2589
2590    /// Returns true if the user with the given user_id is able to send a
2591    /// specific message type in the room.
2592    ///
2593    /// The call may fail if there is an error in getting the power levels.
2594    pub async fn can_user_send_message(
2595        &self,
2596        user_id: &UserId,
2597        message: MessageLikeEventType,
2598    ) -> Result<bool> {
2599        Ok(self.power_levels().await?.user_can_send_message(user_id, message))
2600    }
2601
2602    /// Returns true if the user with the given user_id is able to pin or unpin
2603    /// events in the room.
2604    ///
2605    /// The call may fail if there is an error in getting the power levels.
2606    pub async fn can_user_pin_unpin(&self, user_id: &UserId) -> Result<bool> {
2607        Ok(self
2608            .power_levels()
2609            .await?
2610            .user_can_send_state(user_id, StateEventType::RoomPinnedEvents))
2611    }
2612
2613    /// Returns true if the user with the given user_id is able to trigger a
2614    /// notification in the room.
2615    ///
2616    /// The call may fail if there is an error in getting the power levels.
2617    pub async fn can_user_trigger_room_notification(&self, user_id: &UserId) -> Result<bool> {
2618        Ok(self.power_levels().await?.user_can_trigger_room_notification(user_id))
2619    }
2620
2621    /// Get a list of servers that should know this room.
2622    ///
2623    /// Uses the synced members of the room and the suggested [routing
2624    /// algorithm] from the Matrix spec.
2625    ///
2626    /// Returns at most three servers.
2627    ///
2628    /// [routing algorithm]: https://spec.matrix.org/v1.3/appendices/#routing
2629    pub async fn route(&self) -> Result<Vec<OwnedServerName>> {
2630        let acl_ev = self
2631            .get_state_event_static::<RoomServerAclEventContent>()
2632            .await?
2633            .and_then(|ev| ev.deserialize().ok());
2634        let acl = acl_ev.as_ref().and_then(|ev| match ev {
2635            SyncOrStrippedState::Sync(ev) => ev.as_original().map(|ev| &ev.content),
2636            SyncOrStrippedState::Stripped(ev) => Some(&ev.content),
2637        });
2638
2639        // Filter out server names that:
2640        // - Are blocked due to server ACLs
2641        // - Are IP addresses
2642        let members: Vec<_> = self
2643            .members_no_sync(RoomMemberships::JOIN)
2644            .await?
2645            .into_iter()
2646            .filter(|member| {
2647                let server = member.user_id().server_name();
2648                acl.filter(|acl| !acl.is_allowed(server)).is_none() && !server.is_ip_literal()
2649            })
2650            .collect();
2651
2652        // Get the server of the highest power level user in the room, provided
2653        // they are at least power level 50.
2654        let max = members
2655            .iter()
2656            .max_by_key(|member| member.power_level())
2657            .filter(|max| max.power_level() >= 50)
2658            .map(|member| member.user_id().server_name());
2659
2660        // Sort the servers by population.
2661        let servers = members
2662            .iter()
2663            .map(|member| member.user_id().server_name())
2664            .filter(|server| max.filter(|max| max == server).is_none())
2665            .fold(BTreeMap::<_, u32>::new(), |mut servers, server| {
2666                *servers.entry(server).or_default() += 1;
2667                servers
2668            });
2669        let mut servers: Vec<_> = servers.into_iter().collect();
2670        servers.sort_unstable_by(|(_, count_a), (_, count_b)| count_b.cmp(count_a));
2671
2672        Ok(max
2673            .into_iter()
2674            .chain(servers.into_iter().map(|(name, _)| name))
2675            .take(3)
2676            .map(ToOwned::to_owned)
2677            .collect())
2678    }
2679
2680    /// Get a `matrix.to` permalink to this room.
2681    ///
2682    /// If this room has an alias, we use it. Otherwise, we try to use the
2683    /// synced members in the room for [routing] the room ID.
2684    ///
2685    /// [routing]: https://spec.matrix.org/v1.3/appendices/#routing
2686    pub async fn matrix_to_permalink(&self) -> Result<MatrixToUri> {
2687        if let Some(alias) = self.canonical_alias().or_else(|| self.alt_aliases().pop()) {
2688            return Ok(alias.matrix_to_uri());
2689        }
2690
2691        let via = self.route().await?;
2692        Ok(self.room_id().matrix_to_uri_via(via))
2693    }
2694
2695    /// Get a `matrix:` permalink to this room.
2696    ///
2697    /// If this room has an alias, we use it. Otherwise, we try to use the
2698    /// synced members in the room for [routing] the room ID.
2699    ///
2700    /// # Arguments
2701    ///
2702    /// * `join` - Whether the user should join the room.
2703    ///
2704    /// [routing]: https://spec.matrix.org/v1.3/appendices/#routing
2705    pub async fn matrix_permalink(&self, join: bool) -> Result<MatrixUri> {
2706        if let Some(alias) = self.canonical_alias().or_else(|| self.alt_aliases().pop()) {
2707            return Ok(alias.matrix_uri(join));
2708        }
2709
2710        let via = self.route().await?;
2711        Ok(self.room_id().matrix_uri_via(via, join))
2712    }
2713
2714    /// Get a `matrix.to` permalink to an event in this room.
2715    ///
2716    /// We try to use the synced members in the room for [routing] the room ID.
2717    ///
2718    /// *Note*: This method does not check if the given event ID is actually
2719    /// part of this room. It needs to be checked before calling this method
2720    /// otherwise the permalink won't work.
2721    ///
2722    /// # Arguments
2723    ///
2724    /// * `event_id` - The ID of the event.
2725    ///
2726    /// [routing]: https://spec.matrix.org/v1.3/appendices/#routing
2727    pub async fn matrix_to_event_permalink(
2728        &self,
2729        event_id: impl Into<OwnedEventId>,
2730    ) -> Result<MatrixToUri> {
2731        // Don't use the alias because an event is tied to a room ID, but an
2732        // alias might point to another room, e.g. after a room upgrade.
2733        let via = self.route().await?;
2734        Ok(self.room_id().matrix_to_event_uri_via(event_id, via))
2735    }
2736
2737    /// Get a `matrix:` permalink to an event in this room.
2738    ///
2739    /// We try to use the synced members in the room for [routing] the room ID.
2740    ///
2741    /// *Note*: This method does not check if the given event ID is actually
2742    /// part of this room. It needs to be checked before calling this method
2743    /// otherwise the permalink won't work.
2744    ///
2745    /// # Arguments
2746    ///
2747    /// * `event_id` - The ID of the event.
2748    ///
2749    /// [routing]: https://spec.matrix.org/v1.3/appendices/#routing
2750    pub async fn matrix_event_permalink(
2751        &self,
2752        event_id: impl Into<OwnedEventId>,
2753    ) -> Result<MatrixUri> {
2754        // Don't use the alias because an event is tied to a room ID, but an
2755        // alias might point to another room, e.g. after a room upgrade.
2756        let via = self.route().await?;
2757        Ok(self.room_id().matrix_event_uri_via(event_id, via))
2758    }
2759
2760    /// Get the latest receipt of a user in this room.
2761    ///
2762    /// # Arguments
2763    ///
2764    /// * `receipt_type` - The type of receipt to get.
2765    ///
2766    /// * `thread` - The thread containing the event of the receipt, if any.
2767    ///
2768    /// * `user_id` - The ID of the user.
2769    ///
2770    /// Returns the ID of the event on which the receipt applies and the
2771    /// receipt.
2772    pub async fn load_user_receipt(
2773        &self,
2774        receipt_type: ReceiptType,
2775        thread: ReceiptThread,
2776        user_id: &UserId,
2777    ) -> Result<Option<(OwnedEventId, Receipt)>> {
2778        self.inner.load_user_receipt(receipt_type, thread, user_id).await.map_err(Into::into)
2779    }
2780
2781    /// Load the receipts for an event in this room from storage.
2782    ///
2783    /// # Arguments
2784    ///
2785    /// * `receipt_type` - The type of receipt to get.
2786    ///
2787    /// * `thread` - The thread containing the event of the receipt, if any.
2788    ///
2789    /// * `event_id` - The ID of the event.
2790    ///
2791    /// Returns a list of IDs of users who have sent a receipt for the event and
2792    /// the corresponding receipts.
2793    pub async fn load_event_receipts(
2794        &self,
2795        receipt_type: ReceiptType,
2796        thread: ReceiptThread,
2797        event_id: &EventId,
2798    ) -> Result<Vec<(OwnedUserId, Receipt)>> {
2799        self.inner.load_event_receipts(receipt_type, thread, event_id).await.map_err(Into::into)
2800    }
2801
2802    /// Get the push context for this room.
2803    ///
2804    /// Returns `None` if some data couldn't be found. This should only happen
2805    /// in brand new rooms, while we process its state.
2806    pub async fn push_context(&self) -> Result<Option<PushConditionRoomCtx>> {
2807        let room_id = self.room_id();
2808        let user_id = self.own_user_id();
2809        let room_info = self.clone_info();
2810        let member_count = room_info.active_members_count();
2811
2812        let user_display_name = if let Some(member) = self.get_member_no_sync(user_id).await? {
2813            member.name().to_owned()
2814        } else {
2815            return Ok(None);
2816        };
2817
2818        let power_levels = self
2819            .get_state_event_static::<RoomPowerLevelsEventContent>()
2820            .await?
2821            .and_then(|e| e.deserialize().ok())
2822            .map(|e| e.power_levels().into());
2823
2824        Ok(Some(PushConditionRoomCtx {
2825            user_id: user_id.to_owned(),
2826            room_id: room_id.to_owned(),
2827            member_count: UInt::new(member_count).unwrap_or(UInt::MAX),
2828            user_display_name,
2829            power_levels,
2830        }))
2831    }
2832
2833    /// Get the push actions for the given event with the current room state.
2834    ///
2835    /// Note that it is possible that no push action is returned because the
2836    /// current room state does not have all the required state events.
2837    pub async fn event_push_actions<T>(&self, event: &Raw<T>) -> Result<Option<Vec<Action>>> {
2838        let Some(push_context) = self.push_context().await? else {
2839            debug!("Could not aggregate push context");
2840            return Ok(None);
2841        };
2842
2843        let push_rules = self.client().account().push_rules().await?;
2844
2845        Ok(Some(push_rules.get_actions(event, &push_context).to_owned()))
2846    }
2847
2848    /// The membership details of the (latest) invite for the logged-in user in
2849    /// this room.
2850    pub async fn invite_details(&self) -> Result<Invite> {
2851        let state = self.state();
2852        if state != RoomState::Invited {
2853            return Err(Error::WrongRoomState(WrongRoomState::new("Invited", state)));
2854        }
2855
2856        let invitee = self
2857            .get_member_no_sync(self.own_user_id())
2858            .await?
2859            .ok_or_else(|| Error::UnknownError(Box::new(InvitationError::EventMissing)))?;
2860        let event = invitee.event();
2861        let inviter_id = event.sender();
2862        let inviter = self.get_member_no_sync(inviter_id).await?;
2863        Ok(Invite { invitee, inviter })
2864    }
2865
2866    /// Get the membership details for the current user.
2867    ///
2868    /// Returns:
2869    ///     - If the current user was present in the room, a tuple of the
2870    ///       current user's [`RoomMember`] info and the member info of the
2871    ///       sender of that member event.
2872    ///     - If the current user is not present, an error.
2873    pub async fn own_membership_details(&self) -> Result<(RoomMember, Option<RoomMember>)> {
2874        let Some(own_member) = self.get_member_no_sync(self.own_user_id()).await? else {
2875            return Err(Error::InsufficientData);
2876        };
2877
2878        let sender_member =
2879            if let Some(member) = self.get_member_no_sync(own_member.event().sender()).await? {
2880                // If the sender room member info is already available, return it
2881                Some(member)
2882            } else if self.are_members_synced() {
2883                // The room members are synced and we couldn't find the sender info
2884                None
2885            } else if self.sync_members().await.is_ok() {
2886                // Try getting the sender room member info again after syncing
2887                self.get_member_no_sync(own_member.event().sender()).await?
2888            } else {
2889                None
2890            };
2891
2892        Ok((own_member, sender_member))
2893    }
2894
2895    /// Forget this room.
2896    ///
2897    /// This communicates to the homeserver that it should forget the room.
2898    ///
2899    /// Only left or banned-from rooms can be forgotten.
2900    pub async fn forget(&self) -> Result<()> {
2901        let state = self.state();
2902        match state {
2903            RoomState::Joined | RoomState::Invited | RoomState::Knocked => {
2904                return Err(Error::WrongRoomState(WrongRoomState::new("Left / Banned", state)));
2905            }
2906            RoomState::Left | RoomState::Banned => {}
2907        }
2908
2909        let request = forget_room::v3::Request::new(self.inner.room_id().to_owned());
2910        let _response = self.client.send(request).await?;
2911
2912        // If it was a DM, remove the room from the `m.direct` global account data.
2913        if self.inner.direct_targets_length() != 0 {
2914            if let Err(e) = self.set_is_direct(false).await {
2915                // It is not important whether we managed to remove the room, it will not have
2916                // any consequences, so just log the error.
2917                warn!(room_id = ?self.room_id(), "failed to remove room from m.direct account data: {e}");
2918            }
2919        }
2920
2921        self.client.base_client().forget_room(self.inner.room_id()).await?;
2922
2923        Ok(())
2924    }
2925
2926    fn ensure_room_joined(&self) -> Result<()> {
2927        let state = self.state();
2928        if state == RoomState::Joined {
2929            Ok(())
2930        } else {
2931            Err(Error::WrongRoomState(WrongRoomState::new("Joined", state)))
2932        }
2933    }
2934
2935    /// Get the notification mode.
2936    pub async fn notification_mode(&self) -> Option<RoomNotificationMode> {
2937        if !matches!(self.state(), RoomState::Joined) {
2938            return None;
2939        }
2940
2941        let notification_settings = self.client().notification_settings().await;
2942
2943        // Get the user-defined mode if available
2944        let notification_mode =
2945            notification_settings.get_user_defined_room_notification_mode(self.room_id()).await;
2946
2947        if notification_mode.is_some() {
2948            notification_mode
2949        } else if let Ok(is_encrypted) = self.is_encrypted().await {
2950            // Otherwise, if encrypted status is available, get the default mode for this
2951            // type of room.
2952            // From the point of view of notification settings, a `one-to-one` room is one
2953            // that involves exactly two people.
2954            let is_one_to_one = IsOneToOne::from(self.active_members_count() == 2);
2955            let default_mode = notification_settings
2956                .get_default_room_notification_mode(IsEncrypted::from(is_encrypted), is_one_to_one)
2957                .await;
2958            Some(default_mode)
2959        } else {
2960            None
2961        }
2962    }
2963
2964    /// Get the user-defined notification mode.
2965    ///
2966    /// The result is cached for fast and non-async call. To read the cached
2967    /// result, use
2968    /// [`matrix_sdk_base::Room::cached_user_defined_notification_mode`].
2969    //
2970    // Note for maintainers:
2971    //
2972    // The fact the result is cached is an important property. If you change that in
2973    // the future, please review all calls to this method.
2974    pub async fn user_defined_notification_mode(&self) -> Option<RoomNotificationMode> {
2975        if !matches!(self.state(), RoomState::Joined) {
2976            return None;
2977        }
2978
2979        let notification_settings = self.client().notification_settings().await;
2980
2981        // Get the user-defined mode if available
2982        let mode =
2983            notification_settings.get_user_defined_room_notification_mode(self.room_id()).await;
2984
2985        if let Some(mode) = mode {
2986            self.update_cached_user_defined_notification_mode(mode);
2987        }
2988
2989        mode
2990    }
2991
2992    /// Report an event as inappropriate to the homeserver's administrator.
2993    ///
2994    /// # Arguments
2995    ///
2996    /// * `event_id` - The ID of the event to report.
2997    /// * `score` - The score to rate this content.
2998    /// * `reason` - The reason the content is being reported.
2999    ///
3000    /// # Errors
3001    ///
3002    /// Returns an error if the room is not joined or if an error occurs with
3003    /// the request.
3004    pub async fn report_content(
3005        &self,
3006        event_id: OwnedEventId,
3007        score: Option<ReportedContentScore>,
3008        reason: Option<String>,
3009    ) -> Result<report_content::v3::Response> {
3010        let state = self.state();
3011        if state != RoomState::Joined {
3012            return Err(Error::WrongRoomState(WrongRoomState::new("Joined", state)));
3013        }
3014
3015        let request = report_content::v3::Request::new(
3016            self.inner.room_id().to_owned(),
3017            event_id,
3018            score.map(Into::into),
3019            reason,
3020        );
3021        Ok(self.client.send(request).await?)
3022    }
3023
3024    /// Reports a room as inappropriate to the server.
3025    /// The caller is not required to be joined to the room to report it.
3026    ///
3027    /// # Arguments
3028    ///
3029    /// * `reason` - The reason the room is being reported.
3030    ///
3031    /// # Errors
3032    ///
3033    /// Returns an error if the room is not found or on rate limit
3034    pub async fn report_room(&self, reason: Option<String>) -> Result<report_room::v3::Response> {
3035        let mut request = report_room::v3::Request::new(self.inner.room_id().to_owned());
3036        request.reason = reason;
3037
3038        Ok(self.client.send(request).await?)
3039    }
3040
3041    /// Set a flag on the room to indicate that the user has explicitly marked
3042    /// it as (un)read.
3043    pub async fn set_unread_flag(&self, unread: bool) -> Result<()> {
3044        let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
3045
3046        let content = UnstableMarkedUnreadEventContent::from(MarkedUnreadEventContent::new(unread));
3047
3048        let request = set_room_account_data::v3::Request::new(
3049            user_id.to_owned(),
3050            self.inner.room_id().to_owned(),
3051            &content,
3052        )?;
3053
3054        self.client.send(request).await?;
3055        Ok(())
3056    }
3057
3058    /// Returns the [`RoomEventCache`] associated to this room, assuming the
3059    /// global [`EventCache`] has been enabled for subscription.
3060    pub async fn event_cache(
3061        &self,
3062    ) -> event_cache::Result<(RoomEventCache, Arc<EventCacheDropHandles>)> {
3063        self.client.event_cache().for_room(self.room_id()).await
3064    }
3065
3066    /// This will only send a call notification event if appropriate.
3067    ///
3068    /// This function is supposed to be called whenever the user creates a room
3069    /// call. It will send a `m.call.notify` event if:
3070    ///  - there is not yet a running call.
3071    ///
3072    /// It will configure the notify type: ring or notify based on:
3073    ///  - is this a DM room -> ring
3074    ///  - is this a group with more than one other member -> notify
3075    pub async fn send_call_notification_if_needed(&self) -> Result<()> {
3076        if self.has_active_room_call() {
3077            return Ok(());
3078        }
3079
3080        if !self.can_user_trigger_room_notification(self.own_user_id()).await? {
3081            return Ok(());
3082        }
3083
3084        self.send_call_notification(
3085            self.room_id().to_string().to_owned(),
3086            ApplicationType::Call,
3087            if self.is_direct().await.unwrap_or(false) {
3088                NotifyType::Ring
3089            } else {
3090                NotifyType::Notify
3091            },
3092            Mentions::with_room_mention(),
3093        )
3094        .await?;
3095
3096        Ok(())
3097    }
3098
3099    /// Get the beacon information event in the room for the `user_id`.
3100    ///
3101    /// # Errors
3102    ///
3103    /// Returns an error if the event is redacted, stripped, not found or could
3104    /// not be deserialized.
3105    pub(crate) async fn get_user_beacon_info(
3106        &self,
3107        user_id: &UserId,
3108    ) -> Result<OriginalSyncStateEvent<BeaconInfoEventContent>, BeaconError> {
3109        let raw_event = self
3110            .get_state_event_static_for_key::<BeaconInfoEventContent, _>(user_id)
3111            .await?
3112            .ok_or(BeaconError::NotFound)?;
3113
3114        match raw_event.deserialize()? {
3115            SyncOrStrippedState::Sync(SyncStateEvent::Original(beacon_info)) => Ok(beacon_info),
3116            SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_)) => Err(BeaconError::Redacted),
3117            SyncOrStrippedState::Stripped(_) => Err(BeaconError::Stripped),
3118        }
3119    }
3120
3121    /// Start sharing live location in the room.
3122    ///
3123    /// # Arguments
3124    ///
3125    /// * `duration_millis` - The duration for which the live location is
3126    ///   shared, in milliseconds.
3127    /// * `description` - An optional description for the live location share.
3128    ///
3129    /// # Errors
3130    ///
3131    /// Returns an error if the room is not joined or if the state event could
3132    /// not be sent.
3133    pub async fn start_live_location_share(
3134        &self,
3135        duration_millis: u64,
3136        description: Option<String>,
3137    ) -> Result<send_state_event::v3::Response> {
3138        self.ensure_room_joined()?;
3139
3140        self.send_state_event_for_key(
3141            self.own_user_id(),
3142            BeaconInfoEventContent::new(
3143                description,
3144                Duration::from_millis(duration_millis),
3145                true,
3146                None,
3147            ),
3148        )
3149        .await
3150    }
3151
3152    /// Stop sharing live location in the room.
3153    ///
3154    /// # Errors
3155    ///
3156    /// Returns an error if the room is not joined, if the beacon information
3157    /// is redacted or stripped, or if the state event is not found.
3158    pub async fn stop_live_location_share(
3159        &self,
3160    ) -> Result<send_state_event::v3::Response, BeaconError> {
3161        self.ensure_room_joined()?;
3162
3163        let mut beacon_info_event = self.get_user_beacon_info(self.own_user_id()).await?;
3164        beacon_info_event.content.stop();
3165        Ok(self.send_state_event_for_key(self.own_user_id(), beacon_info_event.content).await?)
3166    }
3167
3168    /// Send a location beacon event in the current room.
3169    ///
3170    /// # Arguments
3171    ///
3172    /// * `geo_uri` - The geo URI of the location beacon.
3173    ///
3174    /// # Errors
3175    ///
3176    /// Returns an error if the room is not joined, if the beacon information
3177    /// is redacted or stripped, if the location share is no longer live,
3178    /// or if the state event is not found.
3179    pub async fn send_location_beacon(
3180        &self,
3181        geo_uri: String,
3182    ) -> Result<send_message_event::v3::Response, BeaconError> {
3183        self.ensure_room_joined()?;
3184
3185        let beacon_info_event = self.get_user_beacon_info(self.own_user_id()).await?;
3186
3187        if beacon_info_event.content.is_live() {
3188            let content = BeaconEventContent::new(beacon_info_event.event_id, geo_uri, None);
3189            Ok(self.send(content).await?)
3190        } else {
3191            Err(BeaconError::NotLive)
3192        }
3193    }
3194
3195    /// Send a call notification event in the current room.
3196    ///
3197    /// This is only supposed to be used in **custom** situations where the user
3198    /// explicitly chooses to send a `m.call.notify` event to invite/notify
3199    /// someone explicitly in unusual conditions. The default should be to
3200    /// use `send_call_notification_if_needed` just before a new room call is
3201    /// created/joined.
3202    ///
3203    /// One example could be that the UI allows to start a call with a subset of
3204    /// users of the room members first. And then later on the user can
3205    /// invite more users to the call.
3206    pub async fn send_call_notification(
3207        &self,
3208        call_id: String,
3209        application: ApplicationType,
3210        notify_type: NotifyType,
3211        mentions: Mentions,
3212    ) -> Result<()> {
3213        let call_notify_event_content =
3214            CallNotifyEventContent::new(call_id, application, notify_type, mentions);
3215        self.send(call_notify_event_content).await?;
3216        Ok(())
3217    }
3218
3219    /// Store the given `ComposerDraft` in the state store using the current
3220    /// room id, as identifier.
3221    pub async fn save_composer_draft(&self, draft: ComposerDraft) -> Result<()> {
3222        self.client
3223            .store()
3224            .set_kv_data(
3225                StateStoreDataKey::ComposerDraft(self.room_id()),
3226                StateStoreDataValue::ComposerDraft(draft),
3227            )
3228            .await?;
3229        Ok(())
3230    }
3231
3232    /// Retrieve the `ComposerDraft` stored in the state store for this room.
3233    pub async fn load_composer_draft(&self) -> Result<Option<ComposerDraft>> {
3234        let data = self
3235            .client
3236            .store()
3237            .get_kv_data(StateStoreDataKey::ComposerDraft(self.room_id()))
3238            .await?;
3239        Ok(data.and_then(|d| d.into_composer_draft()))
3240    }
3241
3242    /// Remove the `ComposerDraft` stored in the state store for this room.
3243    pub async fn clear_composer_draft(&self) -> Result<()> {
3244        self.client
3245            .store()
3246            .remove_kv_data(StateStoreDataKey::ComposerDraft(self.room_id()))
3247            .await?;
3248        Ok(())
3249    }
3250
3251    /// Load pinned state events for a room from the `/state` endpoint in the
3252    /// home server.
3253    pub async fn load_pinned_events(&self) -> Result<Option<Vec<OwnedEventId>>> {
3254        let response = self
3255            .client
3256            .send(get_state_events_for_key::v3::Request::new(
3257                self.room_id().to_owned(),
3258                StateEventType::RoomPinnedEvents,
3259                "".to_owned(),
3260            ))
3261            .await;
3262
3263        match response {
3264            Ok(response) => {
3265                Ok(Some(response.content.deserialize_as::<RoomPinnedEventsEventContent>()?.pinned))
3266            }
3267            Err(http_error) => match http_error.as_client_api_error() {
3268                Some(error) if error.status_code == StatusCode::NOT_FOUND => Ok(None),
3269                _ => Err(http_error.into()),
3270            },
3271        }
3272    }
3273
3274    /// Observe live location sharing events for this room.
3275    ///
3276    /// The returned observable will receive the newest event for each sync
3277    /// response that contains an `m.beacon` event.
3278    ///
3279    /// Returns a stream of [`ObservableLiveLocation`] events from other users
3280    /// in the room, excluding the live location events of the room's own user.
3281    pub fn observe_live_location_shares(&self) -> ObservableLiveLocation {
3282        ObservableLiveLocation::new(&self.client, self.room_id())
3283    }
3284
3285    /// Subscribe to knock requests in this `Room`.
3286    ///
3287    /// The current requests to join the room will be emitted immediately
3288    /// when subscribing.
3289    ///
3290    /// A new set of knock requests will be emitted whenever:
3291    /// - A new member event is received.
3292    /// - A knock request is marked as seen.
3293    /// - A sync is gappy (limited), so room membership information may be
3294    ///   outdated.
3295    ///
3296    /// Returns both a stream of knock requests and a handle for a task that
3297    /// will clean up the seen knock request ids when possible.
3298    pub async fn subscribe_to_knock_requests(
3299        &self,
3300    ) -> Result<(impl Stream<Item = Vec<KnockRequest>>, JoinHandle<()>)> {
3301        let this = Arc::new(self.clone());
3302
3303        let room_member_events_observer =
3304            self.client.observe_room_events::<SyncRoomMemberEvent, (Client, Room)>(this.room_id());
3305
3306        let current_seen_ids = self.get_seen_knock_request_ids().await?;
3307        let mut seen_request_ids_stream = self
3308            .seen_knock_request_ids_map
3309            .subscribe()
3310            .await
3311            .map(|values| values.unwrap_or_default());
3312
3313        let mut room_info_stream = self.subscribe_info();
3314
3315        // Spawn a task that will clean up the seen knock request ids when updated room
3316        // members are received
3317        let clear_seen_ids_handle = spawn({
3318            let this = self.clone();
3319            async move {
3320                let mut member_updates_stream = this.room_member_updates_sender.subscribe();
3321                while member_updates_stream.recv().await.is_ok() {
3322                    // If room members were updated, try to remove outdated seen knock request ids
3323                    if let Err(err) = this.remove_outdated_seen_knock_requests_ids().await {
3324                        warn!("Failed to remove seen knock requests: {err}")
3325                    }
3326                }
3327            }
3328        });
3329
3330        let combined_stream = stream! {
3331            // Emit current requests to join
3332            match this.get_current_join_requests(&current_seen_ids).await {
3333                Ok(initial_requests) => yield initial_requests,
3334                Err(err) => warn!("Failed to get initial requests to join: {err}")
3335            }
3336
3337            let mut requests_stream = room_member_events_observer.subscribe();
3338            let mut seen_ids = current_seen_ids.clone();
3339
3340            loop {
3341                // This is equivalent to a combine stream operation, triggering a new emission
3342                // when any of the branches changes
3343                tokio::select! {
3344                    Some((event, _)) = requests_stream.next() => {
3345                        if let Some(event) = event.as_original() {
3346                            // If we can calculate the membership change, try to emit only when needed
3347                            let emit = if event.prev_content().is_some() {
3348                                matches!(event.membership_change(),
3349                                    MembershipChange::Banned |
3350                                    MembershipChange::Knocked |
3351                                    MembershipChange::KnockAccepted |
3352                                    MembershipChange::KnockDenied |
3353                                    MembershipChange::KnockRetracted
3354                                )
3355                            } else {
3356                                // If we can't calculate the membership change, assume we need to
3357                                // emit updated values
3358                                true
3359                            };
3360
3361                            if emit {
3362                                match this.get_current_join_requests(&seen_ids).await {
3363                                    Ok(requests) => yield requests,
3364                                    Err(err) => {
3365                                        warn!("Failed to get updated knock requests on new member event: {err}")
3366                                    }
3367                                }
3368                            }
3369                        }
3370                    }
3371
3372                    Some(new_seen_ids) = seen_request_ids_stream.next() => {
3373                        // Update the current seen ids
3374                        seen_ids = new_seen_ids;
3375
3376                        // If seen requests have changed we need to recalculate
3377                        // all the knock requests
3378                        match this.get_current_join_requests(&seen_ids).await {
3379                            Ok(requests) => yield requests,
3380                            Err(err) => {
3381                                warn!("Failed to get updated knock requests on seen ids changed: {err}")
3382                            }
3383                        }
3384                    }
3385
3386                    Some(room_info) = room_info_stream.next() => {
3387                        // We need to emit new items when we may have missing room members:
3388                        // this usually happens after a gappy (limited) sync
3389                        if !room_info.are_members_synced() {
3390                            match this.get_current_join_requests(&seen_ids).await {
3391                                Ok(requests) => yield requests,
3392                                Err(err) => {
3393                                    warn!("Failed to get updated knock requests on gappy (limited) sync: {err}")
3394                                }
3395                            }
3396                        }
3397                    }
3398                    // If the streams in all branches are closed, stop the loop
3399                    else => break,
3400                }
3401            }
3402        };
3403
3404        Ok((combined_stream, clear_seen_ids_handle))
3405    }
3406
3407    async fn get_current_join_requests(
3408        &self,
3409        seen_request_ids: &BTreeMap<OwnedEventId, OwnedUserId>,
3410    ) -> Result<Vec<KnockRequest>> {
3411        Ok(self
3412            .members(RoomMemberships::KNOCK)
3413            .await?
3414            .into_iter()
3415            .filter_map(|member| {
3416                let event_id = member.event().event_id()?;
3417                Some(KnockRequest::new(
3418                    self,
3419                    event_id,
3420                    member.event().timestamp(),
3421                    KnockRequestMemberInfo::from_member(&member),
3422                    seen_request_ids.contains_key(event_id),
3423                ))
3424            })
3425            .collect())
3426    }
3427
3428    /// Access the room settings related to privacy and visibility.
3429    pub fn privacy_settings(&self) -> RoomPrivacySettings<'_> {
3430        RoomPrivacySettings::new(&self.inner, &self.client)
3431    }
3432}
3433
3434#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
3435impl RoomIdentityProvider for Room {
3436    fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool> {
3437        Box::pin(async { self.get_member(user_id).await.unwrap_or(None).is_some() })
3438    }
3439
3440    fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>> {
3441        Box::pin(async {
3442            let members = self
3443                .members(RoomMemberships::JOIN | RoomMemberships::INVITE)
3444                .await
3445                .unwrap_or_else(|_| Default::default());
3446
3447            let mut ret: Vec<UserIdentity> = Vec::new();
3448            for member in members {
3449                if let Some(i) = self.user_identity(member.user_id()).await {
3450                    ret.push(i);
3451                }
3452            }
3453            ret
3454        })
3455    }
3456
3457    fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>> {
3458        Box::pin(async {
3459            self.client
3460                .encryption()
3461                .get_user_identity(user_id)
3462                .await
3463                .unwrap_or(None)
3464                .map(|u| u.underlying_identity())
3465        })
3466    }
3467}
3468
3469/// A wrapper for a weak client and a room id that allows to lazily retrieve a
3470/// room, only when needed.
3471#[derive(Clone)]
3472pub(crate) struct WeakRoom {
3473    client: WeakClient,
3474    room_id: OwnedRoomId,
3475}
3476
3477impl WeakRoom {
3478    /// Create a new `WeakRoom` given its weak components.
3479    pub fn new(client: WeakClient, room_id: OwnedRoomId) -> Self {
3480        Self { client, room_id }
3481    }
3482
3483    /// Attempts to reconstruct the room.
3484    pub fn get(&self) -> Option<Room> {
3485        self.client.get().and_then(|client| client.get_room(&self.room_id))
3486    }
3487
3488    /// The room id for that room.
3489    pub fn room_id(&self) -> &RoomId {
3490        &self.room_id
3491    }
3492}
3493
3494/// Details of the (latest) invite.
3495#[derive(Debug, Clone)]
3496pub struct Invite {
3497    /// Who has been invited.
3498    pub invitee: RoomMember,
3499    /// Who sent the invite.
3500    pub inviter: Option<RoomMember>,
3501}
3502
3503#[derive(Error, Debug)]
3504enum InvitationError {
3505    #[error("No membership event found")]
3506    EventMissing,
3507}
3508
3509/// Receipts to send all at once.
3510#[derive(Debug, Clone, Default)]
3511#[non_exhaustive]
3512pub struct Receipts {
3513    /// Fully-read marker (room account data).
3514    pub fully_read: Option<OwnedEventId>,
3515    /// Read receipt (public ephemeral room event).
3516    pub public_read_receipt: Option<OwnedEventId>,
3517    /// Read receipt (private ephemeral room event).
3518    pub private_read_receipt: Option<OwnedEventId>,
3519}
3520
3521impl Receipts {
3522    /// Create an empty `Receipts`.
3523    pub fn new() -> Self {
3524        Self::default()
3525    }
3526
3527    /// Set the last event the user has read.
3528    ///
3529    /// It means that the user has read all the events before this event.
3530    ///
3531    /// This is a private marker only visible by the user.
3532    ///
3533    /// Note that this is technically not a receipt as it is persisted in the
3534    /// room account data.
3535    pub fn fully_read_marker(mut self, event_id: impl Into<Option<OwnedEventId>>) -> Self {
3536        self.fully_read = event_id.into();
3537        self
3538    }
3539
3540    /// Set the last event presented to the user and forward it to the other
3541    /// users in the room.
3542    ///
3543    /// This is used to reset the unread messages/notification count and
3544    /// advertise to other users the last event that the user has likely seen.
3545    pub fn public_read_receipt(mut self, event_id: impl Into<Option<OwnedEventId>>) -> Self {
3546        self.public_read_receipt = event_id.into();
3547        self
3548    }
3549
3550    /// Set the last event presented to the user and don't forward it.
3551    ///
3552    /// This is used to reset the unread messages/notification count.
3553    pub fn private_read_receipt(mut self, event_id: impl Into<Option<OwnedEventId>>) -> Self {
3554        self.private_read_receipt = event_id.into();
3555        self
3556    }
3557
3558    /// Whether this `Receipts` is empty.
3559    pub fn is_empty(&self) -> bool {
3560        self.fully_read.is_none()
3561            && self.public_read_receipt.is_none()
3562            && self.private_read_receipt.is_none()
3563    }
3564}
3565
3566/// [Parent space](https://spec.matrix.org/v1.8/client-server-api/#mspaceparent-relationships)
3567/// listed by a room, possibly validated by checking the space's state.
3568#[derive(Debug)]
3569pub enum ParentSpace {
3570    /// The room recognizes the given room as its parent, and the parent
3571    /// recognizes it as its child.
3572    Reciprocal(Room),
3573    /// The room recognizes the given room as its parent, but the parent does
3574    /// not recognizes it as its child. However, the author of the
3575    /// `m.room.parent` event in the room has a sufficient power level in the
3576    /// parent to create the child event.
3577    WithPowerlevel(Room),
3578    /// The room recognizes the given room as its parent, but the parent does
3579    /// not recognizes it as its child.
3580    Illegitimate(Room),
3581    /// The room recognizes the given id as its parent room, but we cannot check
3582    /// whether the parent recognizes it as its child.
3583    Unverifiable(OwnedRoomId),
3584}
3585
3586/// The score to rate an inappropriate content.
3587///
3588/// Must be a value between `0`, inoffensive, and `-100`, very offensive.
3589#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
3590pub struct ReportedContentScore(i8);
3591
3592impl ReportedContentScore {
3593    /// The smallest value that can be represented by this type.
3594    ///
3595    /// This is for very offensive content.
3596    pub const MIN: Self = Self(-100);
3597
3598    /// The largest value that can be represented by this type.
3599    ///
3600    /// This is for inoffensive content.
3601    pub const MAX: Self = Self(0);
3602
3603    /// Try to create a `ReportedContentScore` from the provided `i8`.
3604    ///
3605    /// Returns `None` if it is smaller than [`ReportedContentScore::MIN`] or
3606    /// larger than [`ReportedContentScore::MAX`] .
3607    ///
3608    /// This is the same as the `TryFrom<i8>` implementation for
3609    /// `ReportedContentScore`, except that it returns an `Option` instead
3610    /// of a `Result`.
3611    pub fn new(value: i8) -> Option<Self> {
3612        value.try_into().ok()
3613    }
3614
3615    /// Create a `ReportedContentScore` from the provided `i8` clamped to the
3616    /// acceptable interval.
3617    ///
3618    /// The given value gets clamped into the closed interval between
3619    /// [`ReportedContentScore::MIN`] and [`ReportedContentScore::MAX`].
3620    pub fn new_saturating(value: i8) -> Self {
3621        if value > Self::MAX {
3622            Self::MAX
3623        } else if value < Self::MIN {
3624            Self::MIN
3625        } else {
3626            Self(value)
3627        }
3628    }
3629
3630    /// The value of this score.
3631    pub fn value(&self) -> i8 {
3632        self.0
3633    }
3634}
3635
3636impl PartialEq<i8> for ReportedContentScore {
3637    fn eq(&self, other: &i8) -> bool {
3638        self.0.eq(other)
3639    }
3640}
3641
3642impl PartialEq<ReportedContentScore> for i8 {
3643    fn eq(&self, other: &ReportedContentScore) -> bool {
3644        self.eq(&other.0)
3645    }
3646}
3647
3648impl PartialOrd<i8> for ReportedContentScore {
3649    fn partial_cmp(&self, other: &i8) -> Option<std::cmp::Ordering> {
3650        self.0.partial_cmp(other)
3651    }
3652}
3653
3654impl PartialOrd<ReportedContentScore> for i8 {
3655    fn partial_cmp(&self, other: &ReportedContentScore) -> Option<std::cmp::Ordering> {
3656        self.partial_cmp(&other.0)
3657    }
3658}
3659
3660impl From<ReportedContentScore> for Int {
3661    fn from(value: ReportedContentScore) -> Self {
3662        value.0.into()
3663    }
3664}
3665
3666impl TryFrom<i8> for ReportedContentScore {
3667    type Error = TryFromReportedContentScoreError;
3668
3669    fn try_from(value: i8) -> std::prelude::v1::Result<Self, Self::Error> {
3670        if value > Self::MAX || value < Self::MIN {
3671            Err(TryFromReportedContentScoreError(()))
3672        } else {
3673            Ok(Self(value))
3674        }
3675    }
3676}
3677
3678impl TryFrom<i16> for ReportedContentScore {
3679    type Error = TryFromReportedContentScoreError;
3680
3681    fn try_from(value: i16) -> std::prelude::v1::Result<Self, Self::Error> {
3682        let value = i8::try_from(value).map_err(|_| TryFromReportedContentScoreError(()))?;
3683        value.try_into()
3684    }
3685}
3686
3687impl TryFrom<i32> for ReportedContentScore {
3688    type Error = TryFromReportedContentScoreError;
3689
3690    fn try_from(value: i32) -> std::prelude::v1::Result<Self, Self::Error> {
3691        let value = i8::try_from(value).map_err(|_| TryFromReportedContentScoreError(()))?;
3692        value.try_into()
3693    }
3694}
3695
3696impl TryFrom<i64> for ReportedContentScore {
3697    type Error = TryFromReportedContentScoreError;
3698
3699    fn try_from(value: i64) -> std::prelude::v1::Result<Self, Self::Error> {
3700        let value = i8::try_from(value).map_err(|_| TryFromReportedContentScoreError(()))?;
3701        value.try_into()
3702    }
3703}
3704
3705impl TryFrom<Int> for ReportedContentScore {
3706    type Error = TryFromReportedContentScoreError;
3707
3708    fn try_from(value: Int) -> std::prelude::v1::Result<Self, Self::Error> {
3709        let value = i8::try_from(value).map_err(|_| TryFromReportedContentScoreError(()))?;
3710        value.try_into()
3711    }
3712}
3713
3714/// The error type returned when a checked `ReportedContentScore` conversion
3715/// fails.
3716#[derive(Debug, Clone, Error)]
3717#[error("out of range conversion attempted")]
3718pub struct TryFromReportedContentScoreError(());
3719
3720#[cfg(all(test, not(target_arch = "wasm32")))]
3721mod tests {
3722    use assert_matches2::assert_matches;
3723    use matrix_sdk_base::{store::ComposerDraftType, ComposerDraft};
3724    use matrix_sdk_test::{
3725        async_test, event_factory::EventFactory, test_json, JoinedRoomBuilder, StateTestEvent,
3726        SyncResponseBuilder,
3727    };
3728    use ruma::{event_id, events::room::member::MembershipState, int, room_id, user_id};
3729    use wiremock::{
3730        matchers::{header, method, path_regex},
3731        Mock, MockServer, ResponseTemplate,
3732    };
3733
3734    use super::ReportedContentScore;
3735    use crate::{
3736        config::RequestConfig,
3737        test_utils::{client::mock_matrix_session, logged_in_client, mocks::MatrixMockServer},
3738        Client,
3739    };
3740
3741    #[cfg(all(feature = "sqlite", feature = "e2e-encryption"))]
3742    #[async_test]
3743    async fn test_cache_invalidation_while_encrypt() {
3744        use matrix_sdk_test::{message_like_event_content, DEFAULT_TEST_ROOM_ID};
3745
3746        let sqlite_path = std::env::temp_dir().join("cache_invalidation_while_encrypt.db");
3747        let session = mock_matrix_session();
3748
3749        let client = Client::builder()
3750            .homeserver_url("http://localhost:1234")
3751            .request_config(RequestConfig::new().disable_retry())
3752            .sqlite_store(&sqlite_path, None)
3753            .build()
3754            .await
3755            .unwrap();
3756        client.matrix_auth().restore_session(session.clone()).await.unwrap();
3757
3758        client.encryption().enable_cross_process_store_lock("client1".to_owned()).await.unwrap();
3759
3760        // Mock receiving an event to create an internal room.
3761        let server = MockServer::start().await;
3762        {
3763            Mock::given(method("GET"))
3764                .and(path_regex(r"^/_matrix/client/r0/rooms/.*/state/m.*room.*encryption.?"))
3765                .and(header("authorization", "Bearer 1234"))
3766                .respond_with(
3767                    ResponseTemplate::new(200)
3768                        .set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
3769                )
3770                .mount(&server)
3771                .await;
3772            let response = SyncResponseBuilder::default()
3773                .add_joined_room(
3774                    JoinedRoomBuilder::default()
3775                        .add_state_event(StateTestEvent::Member)
3776                        .add_state_event(StateTestEvent::PowerLevels)
3777                        .add_state_event(StateTestEvent::Encryption),
3778                )
3779                .build_sync_response();
3780            client.base_client().receive_sync_response(response).await.unwrap();
3781        }
3782
3783        let room = client.get_room(&DEFAULT_TEST_ROOM_ID).expect("Room should exist");
3784
3785        // Step 1, preshare the room keys.
3786        room.preshare_room_key().await.unwrap();
3787
3788        // Step 2, force lock invalidation by pretending another client obtained the
3789        // lock.
3790        {
3791            let client = Client::builder()
3792                .homeserver_url("http://localhost:1234")
3793                .request_config(RequestConfig::new().disable_retry())
3794                .sqlite_store(&sqlite_path, None)
3795                .build()
3796                .await
3797                .unwrap();
3798            client.matrix_auth().restore_session(session.clone()).await.unwrap();
3799            client
3800                .encryption()
3801                .enable_cross_process_store_lock("client2".to_owned())
3802                .await
3803                .unwrap();
3804
3805            let guard = client.encryption().spin_lock_store(None).await.unwrap();
3806            assert!(guard.is_some());
3807        }
3808
3809        // Step 3, take the crypto-store lock.
3810        let guard = client.encryption().spin_lock_store(None).await.unwrap();
3811        assert!(guard.is_some());
3812
3813        // Step 4, try to encrypt a message.
3814        let olm = client.olm_machine().await;
3815        let olm = olm.as_ref().expect("Olm machine wasn't started");
3816
3817        // Now pretend we're encrypting an event; the olm machine shouldn't rely on
3818        // caching the outgoing session before.
3819        let _encrypted_content = olm
3820            .encrypt_room_event_raw(room.room_id(), "test-event", &message_like_event_content!({}))
3821            .await
3822            .unwrap();
3823    }
3824
3825    #[test]
3826    fn reported_content_score() {
3827        // i8
3828        let score = ReportedContentScore::new(0).unwrap();
3829        assert_eq!(score.value(), 0);
3830        let score = ReportedContentScore::new(-50).unwrap();
3831        assert_eq!(score.value(), -50);
3832        let score = ReportedContentScore::new(-100).unwrap();
3833        assert_eq!(score.value(), -100);
3834        assert_eq!(ReportedContentScore::new(10), None);
3835        assert_eq!(ReportedContentScore::new(-110), None);
3836
3837        let score = ReportedContentScore::new_saturating(0);
3838        assert_eq!(score.value(), 0);
3839        let score = ReportedContentScore::new_saturating(-50);
3840        assert_eq!(score.value(), -50);
3841        let score = ReportedContentScore::new_saturating(-100);
3842        assert_eq!(score.value(), -100);
3843        let score = ReportedContentScore::new_saturating(10);
3844        assert_eq!(score, ReportedContentScore::MAX);
3845        let score = ReportedContentScore::new_saturating(-110);
3846        assert_eq!(score, ReportedContentScore::MIN);
3847
3848        // i16
3849        let score = ReportedContentScore::try_from(0i16).unwrap();
3850        assert_eq!(score.value(), 0);
3851        let score = ReportedContentScore::try_from(-100i16).unwrap();
3852        assert_eq!(score.value(), -100);
3853        ReportedContentScore::try_from(10i16).unwrap_err();
3854        ReportedContentScore::try_from(-110i16).unwrap_err();
3855
3856        // i32
3857        let score = ReportedContentScore::try_from(0i32).unwrap();
3858        assert_eq!(score.value(), 0);
3859        let score = ReportedContentScore::try_from(-100i32).unwrap();
3860        assert_eq!(score.value(), -100);
3861        ReportedContentScore::try_from(10i32).unwrap_err();
3862        ReportedContentScore::try_from(-110i32).unwrap_err();
3863
3864        // i64
3865        let score = ReportedContentScore::try_from(0i64).unwrap();
3866        assert_eq!(score.value(), 0);
3867        let score = ReportedContentScore::try_from(-100i64).unwrap();
3868        assert_eq!(score.value(), -100);
3869        ReportedContentScore::try_from(10i64).unwrap_err();
3870        ReportedContentScore::try_from(-110i64).unwrap_err();
3871
3872        // Int
3873        let score = ReportedContentScore::try_from(int!(0)).unwrap();
3874        assert_eq!(score.value(), 0);
3875        let score = ReportedContentScore::try_from(int!(-100)).unwrap();
3876        assert_eq!(score.value(), -100);
3877        ReportedContentScore::try_from(int!(10)).unwrap_err();
3878        ReportedContentScore::try_from(int!(-110)).unwrap_err();
3879    }
3880
3881    #[async_test]
3882    async fn test_composer_draft() {
3883        use matrix_sdk_test::DEFAULT_TEST_ROOM_ID;
3884
3885        let client = logged_in_client(None).await;
3886
3887        let response = SyncResponseBuilder::default()
3888            .add_joined_room(JoinedRoomBuilder::default())
3889            .build_sync_response();
3890        client.base_client().receive_sync_response(response).await.unwrap();
3891        let room = client.get_room(&DEFAULT_TEST_ROOM_ID).expect("Room should exist");
3892
3893        assert_eq!(room.load_composer_draft().await.unwrap(), None);
3894
3895        let draft = ComposerDraft {
3896            plain_text: "Hello, world!".to_owned(),
3897            html_text: Some("<strong>Hello</strong>, world!".to_owned()),
3898            draft_type: ComposerDraftType::NewMessage,
3899        };
3900        room.save_composer_draft(draft.clone()).await.unwrap();
3901        assert_eq!(room.load_composer_draft().await.unwrap(), Some(draft));
3902
3903        room.clear_composer_draft().await.unwrap();
3904        assert_eq!(room.load_composer_draft().await.unwrap(), None);
3905    }
3906
3907    #[async_test]
3908    async fn test_mark_join_requests_as_seen() {
3909        let server = MatrixMockServer::new().await;
3910        let client = server.client_builder().build().await;
3911        let event_id = event_id!("$a:b.c");
3912        let room_id = room_id!("!a:b.c");
3913        let user_id = user_id!("@alice:b.c");
3914
3915        let f = EventFactory::new().room(room_id);
3916        let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_bulk(vec![f
3917            .member(user_id)
3918            .membership(MembershipState::Knock)
3919            .event_id(event_id)
3920            .into_raw_timeline()
3921            .cast()]);
3922        let room = server.sync_room(&client, joined_room_builder).await;
3923
3924        // When loading the initial seen ids, there are none
3925        let seen_ids =
3926            room.get_seen_knock_request_ids().await.expect("Couldn't load seen join request ids");
3927        assert!(seen_ids.is_empty());
3928
3929        // We mark a random event id as seen
3930        room.mark_knock_requests_as_seen(&[user_id.to_owned()])
3931            .await
3932            .expect("Couldn't mark join request as seen");
3933
3934        // Then we can check it was successfully marked as seen
3935        let seen_ids =
3936            room.get_seen_knock_request_ids().await.expect("Couldn't load seen join request ids");
3937        assert_eq!(seen_ids.len(), 1);
3938        assert_eq!(
3939            seen_ids.into_iter().next().expect("No next value"),
3940            (event_id.to_owned(), user_id.to_owned())
3941        )
3942    }
3943
3944    #[async_test]
3945    async fn test_own_room_membership_with_no_own_member_event() {
3946        let server = MatrixMockServer::new().await;
3947        let client = server.client_builder().build().await;
3948        let room_id = room_id!("!a:b.c");
3949
3950        let room = server.sync_joined_room(&client, room_id).await;
3951
3952        // Since there is no member event for the own user, the method fails.
3953        // This should never happen in an actual room.
3954        let error = room.own_membership_details().await.err();
3955        assert!(error.is_some());
3956    }
3957
3958    #[async_test]
3959    async fn test_own_room_membership_with_own_member_event_but_unknown_sender() {
3960        let server = MatrixMockServer::new().await;
3961        let client = server.client_builder().build().await;
3962        let room_id = room_id!("!a:b.c");
3963        let user_id = user_id!("@example:localhost");
3964
3965        let f = EventFactory::new().room(room_id).sender(user_id!("@alice:b.c"));
3966        let joined_room_builder = JoinedRoomBuilder::new(room_id)
3967            .add_state_bulk(vec![f.member(user_id).into_raw_sync().cast()]);
3968        let room = server.sync_room(&client, joined_room_builder).await;
3969
3970        // When we load the membership details
3971        let ret = room.own_membership_details().await;
3972        assert_matches!(ret, Ok((member, sender)));
3973
3974        // We get the member info for the current user
3975        assert_eq!(member.event().user_id(), user_id);
3976
3977        // But there is no info for the sender
3978        assert!(sender.is_none());
3979    }
3980
3981    #[async_test]
3982    async fn test_own_room_membership_with_own_member_event_and_own_sender() {
3983        let server = MatrixMockServer::new().await;
3984        let client = server.client_builder().build().await;
3985        let room_id = room_id!("!a:b.c");
3986        let user_id = user_id!("@example:localhost");
3987
3988        let f = EventFactory::new().room(room_id).sender(user_id);
3989        let joined_room_builder = JoinedRoomBuilder::new(room_id)
3990            .add_state_bulk(vec![f.member(user_id).into_raw_sync().cast()]);
3991        let room = server.sync_room(&client, joined_room_builder).await;
3992
3993        // When we load the membership details
3994        let ret = room.own_membership_details().await;
3995        assert_matches!(ret, Ok((member, sender)));
3996
3997        // We get the current user's member info
3998        assert_eq!(member.event().user_id(), user_id);
3999
4000        // And the sender has the same info, since it's also the current user
4001        assert!(sender.is_some());
4002        assert_eq!(sender.unwrap().event().user_id(), user_id);
4003    }
4004
4005    #[async_test]
4006    async fn test_own_room_membership_with_own_member_event_and_known_sender() {
4007        let server = MatrixMockServer::new().await;
4008        let client = server.client_builder().build().await;
4009        let room_id = room_id!("!a:b.c");
4010        let user_id = user_id!("@example:localhost");
4011        let sender_id = user_id!("@alice:b.c");
4012
4013        let f = EventFactory::new().room(room_id).sender(sender_id);
4014        let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_bulk(vec![
4015            f.member(user_id).into_raw_sync().cast(),
4016            // The sender info comes from the sync
4017            f.member(sender_id).into_raw_sync().cast(),
4018        ]);
4019        let room = server.sync_room(&client, joined_room_builder).await;
4020
4021        // When we load the membership details
4022        let ret = room.own_membership_details().await;
4023        assert_matches!(ret, Ok((member, sender)));
4024
4025        // We get the current user's member info
4026        assert_eq!(member.event().user_id(), user_id);
4027
4028        // And also the sender info from the events received in the sync
4029        assert!(sender.is_some());
4030        assert_eq!(sender.unwrap().event().user_id(), sender_id);
4031    }
4032
4033    #[async_test]
4034    async fn test_own_room_membership_with_own_member_event_and_unknown_but_available_sender() {
4035        let server = MatrixMockServer::new().await;
4036        let client = server.client_builder().build().await;
4037        let room_id = room_id!("!a:b.c");
4038        let user_id = user_id!("@example:localhost");
4039        let sender_id = user_id!("@alice:b.c");
4040
4041        let f = EventFactory::new().room(room_id).sender(sender_id);
4042        let joined_room_builder = JoinedRoomBuilder::new(room_id)
4043            .add_state_bulk(vec![f.member(user_id).into_raw_sync().cast()]);
4044        let room = server.sync_room(&client, joined_room_builder).await;
4045
4046        // We'll receive the member info through the /members endpoint
4047        server
4048            .mock_get_members()
4049            .ok(vec![f.member(sender_id).into_raw_timeline().cast()])
4050            .mock_once()
4051            .mount()
4052            .await;
4053
4054        // We get the current user's member info
4055        let ret = room.own_membership_details().await;
4056        assert_matches!(ret, Ok((member, sender)));
4057
4058        // We get the current user's member info
4059        assert_eq!(member.event().user_id(), user_id);
4060
4061        // And also the sender info from the /members endpoint
4062        assert!(sender.is_some());
4063        assert_eq!(sender.unwrap().event().user_id(), sender_id);
4064    }
4065}