matrix_sdk/room/
member.rs

1use std::ops::Deref;
2
3use ruma::{
4    events::room::{power_levels::UserPowerLevel, MediaSource},
5    int,
6};
7
8use crate::{
9    media::{MediaFormat, MediaRequestParameters},
10    BaseRoomMember, Client, Result,
11};
12
13/// The high-level `RoomMember` representation
14#[derive(Debug, Clone)]
15pub struct RoomMember {
16    inner: BaseRoomMember,
17    pub(crate) client: Client,
18}
19
20impl Deref for RoomMember {
21    type Target = BaseRoomMember;
22
23    fn deref(&self) -> &Self::Target {
24        &self.inner
25    }
26}
27
28impl RoomMember {
29    pub(crate) fn new(client: Client, member: BaseRoomMember) -> Self {
30        Self { inner: member, client }
31    }
32
33    /// Gets the avatar of this member, if set.
34    ///
35    /// Returns the avatar.
36    /// If a thumbnail is requested no guarantee on the size of the image is
37    /// given.
38    ///
39    /// # Arguments
40    ///
41    /// * `format` - The desired format of the avatar.
42    ///
43    /// # Examples
44    ///
45    /// ```no_run
46    /// use matrix_sdk::{
47    ///     media::MediaFormat, room::RoomMember, ruma::room_id, Client,
48    ///     RoomMemberships,
49    /// };
50    /// # use url::Url;
51    /// # let homeserver = Url::parse("http://example.com").unwrap();
52    /// # async {
53    /// # let user = "example";
54    /// let client = Client::new(homeserver).await.unwrap();
55    /// client.matrix_auth().login_username(user, "password").send().await.unwrap();
56    /// let room_id = room_id!("!roomid:example.com");
57    /// let room = client.get_room(&room_id).unwrap();
58    /// let members = room.members(RoomMemberships::empty()).await.unwrap();
59    /// let member = members.first().unwrap();
60    /// if let Some(avatar) = member.avatar(MediaFormat::File).await.unwrap() {
61    ///     std::fs::write("avatar.png", avatar);
62    /// }
63    /// # };
64    /// ```
65    pub async fn avatar(&self, format: MediaFormat) -> Result<Option<Vec<u8>>> {
66        let Some(url) = self.avatar_url() else { return Ok(None) };
67        let request = MediaRequestParameters { source: MediaSource::Plain(url.to_owned()), format };
68        Ok(Some(self.client.media().get_media_content(&request, true).await?))
69    }
70
71    /// Adds the room member to the current account data's ignore list
72    /// which will ignore the user across all rooms.
73    pub async fn ignore(&self) -> Result<()> {
74        self.client.account().ignore_user(self.inner.user_id()).await
75    }
76
77    /// Removes the room member from the current account data's ignore list
78    /// which will unignore the user across all rooms.
79    pub async fn unignore(&self) -> Result<()> {
80        self.client.account().unignore_user(self.inner.user_id()).await
81    }
82
83    /// Returns true if the member of the room is the user of the account
84    pub fn is_account_user(&self) -> bool {
85        match self.client.user_id() {
86            Some(id) => id == self.inner.user_id(),
87            None => false,
88        }
89    }
90
91    /// Get the suggested role of this member based on their power level.
92    pub fn suggested_role_for_power_level(&self) -> RoomMemberRole {
93        RoomMemberRole::suggested_role_for_power_level(self.power_level())
94    }
95}
96
97/// The role of a member in a room.
98#[derive(Clone, Debug, PartialEq)]
99#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
100pub enum RoomMemberRole {
101    /// The member is a creator.
102    ///
103    /// A creator has an infinite power level and cannot be demoted, so this
104    /// role is immutable. A room can have several creators.
105    ///
106    /// It is available in room versions where
107    /// `explicitly_privilege_room_creators` in [`AuthorizationRules`] is set to
108    /// `true`.
109    ///
110    /// [`AuthorizationRules`]: ruma::room_version_rules::AuthorizationRules
111    Creator,
112    /// The member is an administrator.
113    Administrator,
114    /// The member is a moderator.
115    Moderator,
116    /// The member is a regular user.
117    User,
118}
119
120impl RoomMemberRole {
121    /// Creates the suggested role for a given power level.
122    pub fn suggested_role_for_power_level(power_level: UserPowerLevel) -> Self {
123        match power_level {
124            UserPowerLevel::Infinite => RoomMemberRole::Creator,
125            UserPowerLevel::Int(value) => {
126                if value >= int!(100) {
127                    Self::Administrator
128                } else if value >= int!(50) {
129                    Self::Moderator
130                } else {
131                    Self::User
132                }
133            }
134            // This branch is only necessary because the enum is non-exhaustive.
135            // TODO: Use the `non_exhaustive_omitted_patterns` lint when it becomes stable to be
136            // warned when a variant is added.
137            // Tracking issue: https://github.com/rust-lang/rust/issues/89554
138            _ => unimplemented!(),
139        }
140    }
141
142    /// Get the suggested power level for this role.
143    pub fn suggested_power_level(&self) -> UserPowerLevel {
144        match self {
145            Self::Creator => UserPowerLevel::Infinite,
146            Self::Administrator => UserPowerLevel::Int(int!(100)),
147            Self::Moderator => UserPowerLevel::Int(int!(50)),
148            Self::User => UserPowerLevel::Int(int!(0)),
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_suggested_roles() {
159        assert_eq!(
160            RoomMemberRole::Administrator,
161            RoomMemberRole::suggested_role_for_power_level(int!(100).into())
162        );
163        assert_eq!(
164            RoomMemberRole::Moderator,
165            RoomMemberRole::suggested_role_for_power_level(int!(50).into())
166        );
167        assert_eq!(
168            RoomMemberRole::User,
169            RoomMemberRole::suggested_role_for_power_level(int!(0).into())
170        );
171    }
172
173    #[test]
174    fn test_unexpected_power_levels() {
175        assert_eq!(
176            RoomMemberRole::Administrator,
177            RoomMemberRole::suggested_role_for_power_level(int!(200).into())
178        );
179        assert_eq!(
180            RoomMemberRole::Administrator,
181            RoomMemberRole::suggested_role_for_power_level(int!(101).into())
182        );
183        assert_eq!(
184            RoomMemberRole::Moderator,
185            RoomMemberRole::suggested_role_for_power_level(int!(99).into())
186        );
187        assert_eq!(
188            RoomMemberRole::Moderator,
189            RoomMemberRole::suggested_role_for_power_level(int!(51).into())
190        );
191        assert_eq!(
192            RoomMemberRole::User,
193            RoomMemberRole::suggested_role_for_power_level(int!(-1).into())
194        );
195        assert_eq!(
196            RoomMemberRole::User,
197            RoomMemberRole::suggested_role_for_power_level(int!(-100).into())
198        );
199    }
200
201    #[test]
202    fn test_default_power_levels() {
203        assert_eq!(int!(100), RoomMemberRole::Administrator.suggested_power_level());
204        assert_eq!(int!(50), RoomMemberRole::Moderator.suggested_power_level());
205        assert_eq!(int!(0), RoomMemberRole::User.suggested_power_level());
206
207        assert_eq!(
208            RoomMemberRole::Administrator,
209            RoomMemberRole::suggested_role_for_power_level(
210                RoomMemberRole::Administrator.suggested_power_level()
211            )
212        );
213        assert_eq!(
214            RoomMemberRole::Moderator,
215            RoomMemberRole::suggested_role_for_power_level(
216                RoomMemberRole::Moderator.suggested_power_level()
217            )
218        );
219        assert_eq!(
220            RoomMemberRole::User,
221            RoomMemberRole::suggested_role_for_power_level(
222                RoomMemberRole::User.suggested_power_level()
223            )
224        );
225    }
226}