Skip to main content

ruma_client_api/presence/
get_presence.rs

1//! `GET /_matrix/client/*/presence/{userId}/status`
2//!
3//! Get presence status for this user.
4
5pub mod v3 {
6    //! `/v3/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/v1.19/client-server-api/#get_matrixclientv3presenceuseridstatus
9
10    use std::time::Duration;
11
12    #[cfg(feature = "unstable-msc4532")]
13    use ruma_common::presence::PresenceStatus;
14    use ruma_common::{
15        OwnedUserId,
16        api::{auth_scheme::AccessToken, request, response},
17        metadata,
18        presence::PresenceState,
19    };
20    use serde::{Deserialize, Deserializer, Serialize, Serializer};
21
22    metadata! {
23        method: GET,
24        rate_limited: false,
25        authentication: AccessToken,
26        history: {
27            1.0 => "/_matrix/client/r0/presence/{user_id}/status",
28            1.1 => "/_matrix/client/v3/presence/{user_id}/status",
29        }
30    }
31
32    /// Request type for the `get_presence` endpoint.
33    #[request]
34    pub struct Request {
35        /// The user whose presence state will be retrieved.
36        #[ruma_api(path)]
37        pub user_id: OwnedUserId,
38
39        /// Whether to use [MSC4532]'s revised presence states in responses (if the server supports
40        /// them).
41        ///
42        /// Defaults to `true`.
43        ///
44        /// This uses the unstable prefix defined in [MSC4532].
45        ///
46        /// [MSC4532]: https://github.com/matrix-org/matrix-spec-proposals/pull/4532
47        #[cfg(feature = "unstable-msc4532")]
48        #[serde(
49            default,
50            skip_serializing_if = "ruma_common::serde::is_default",
51            rename = "org.continuwuity.presence_v2.msc4532.revised_presence"
52        )]
53        #[ruma_api(query)]
54        pub revised_presence: bool,
55    }
56
57    /// Response type for the `get_presence` endpoint.
58    #[response]
59    #[ruma_api(manual_body_serde)]
60    pub struct Response {
61        /// The state message for this user if one was set.
62        ///
63        /// If the `unstable-msc4532` feature is enabled, this field will always have the same
64        /// value as `status.msg` after deserialization.
65        #[cfg_attr(
66            feature = "unstable-msc4532",
67            deprecated(note = "Deprecated when MSC4532 is enabled, use `status` instead")
68        )]
69        // required to prevent dead code warnings for the deprecated field on `ResponseBody`
70        #[allow(dead_code)]
71        pub status_msg: Option<String>,
72
73        /// The status information for this user's presence.
74        ///
75        /// This field uses the unstable prefix defined in [MSC4532].
76        ///
77        /// If this field is not present at deserialization, the value of `status_msg`
78        /// will be used instead.
79        ///
80        /// [MSC4532]: https://github.com/matrix-org/matrix-spec-proposals/pull/4532
81        #[cfg(feature = "unstable-msc4532")]
82        pub status: PresenceStatus,
83
84        /// Whether the user is currently active or not.
85        ///
86        /// If the `unstable-msc4532` feature is enabled and `presence` is a [MSC4532]
87        /// presence state, this field will be ignored and will always have the same value
88        /// as [`PresenceState::currently_active`] after deserialization.
89        ///
90        /// [MSC4532]: https://github.com/matrix-org/matrix-spec-proposals/pull/4532
91        #[cfg_attr(
92            feature = "unstable-msc4532",
93            deprecated(
94                note = "Deprecated when MSC4532 is enabled, use `PresenceState::currently_active` instead"
95            )
96        )]
97        pub currently_active: Option<bool>,
98
99        /// The length of time in milliseconds since an action was performed by the user.
100        pub last_active_ago: Option<Duration>,
101
102        /// The user's presence state.
103        pub presence: PresenceState,
104    }
105
106    impl Request {
107        /// Creates a new `Request` with the given user ID.
108        pub fn new(user_id: OwnedUserId) -> Self {
109            Self {
110                user_id,
111                #[cfg(feature = "unstable-msc4532")]
112                revised_presence: true,
113            }
114        }
115    }
116
117    impl Response {
118        /// Creates a new `Response` with the given presence state.
119        pub fn new(presence: PresenceState) -> Self {
120            #[allow(deprecated)]
121            Self {
122                presence,
123                status_msg: None,
124                #[cfg(feature = "unstable-msc4532")]
125                status: PresenceStatus::default(),
126                currently_active: None,
127                last_active_ago: None,
128            }
129        }
130    }
131
132    #[derive(Serialize, Deserialize)]
133    struct ResponseBodyRepr {
134        /// The state message for this user if one was set.
135        #[serde(skip_serializing_if = "Option::is_none")]
136        status_msg: Option<String>,
137
138        /// The status information for this user's presence.
139        ///
140        /// This field uses the unstable prefix defined in [MSC4532].
141        ///
142        /// If this field is not present at deserialization, the value of `status_msg`
143        /// will be used instead.
144        ///
145        /// [MSC4532]: https://github.com/matrix-org/matrix-spec-proposals/pull/4532
146        #[serde(
147            skip_serializing_if = "ruma_common::serde::is_default",
148            rename = "org.continuwuity.presence_v2.msc4532.status",
149            default
150        )]
151        #[cfg(feature = "unstable-msc4532")]
152        status: PresenceStatus,
153
154        /// Whether the user is currently active or not.
155        #[serde(skip_serializing_if = "Option::is_none")]
156        currently_active: Option<bool>,
157
158        /// The length of time in milliseconds since an action was performed by the user.
159        #[serde(
160            with = "ruma_common::serde::duration::opt_ms",
161            default,
162            skip_serializing_if = "Option::is_none"
163        )]
164        last_active_ago: Option<Duration>,
165
166        /// The user's presence state.
167        presence: PresenceState,
168    }
169
170    impl<'de> Deserialize<'de> for ResponseBody {
171        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
172        where
173            D: Deserializer<'de>,
174        {
175            #[allow(deprecated)]
176            let ResponseBodyRepr {
177                status_msg,
178                #[cfg(feature = "unstable-msc4532")]
179                status,
180                currently_active,
181                last_active_ago,
182                presence,
183            } = ResponseBodyRepr::deserialize(deserializer)?;
184
185            #[cfg(feature = "unstable-msc4532")]
186            let status = if status == PresenceStatus::default() {
187                PresenceStatus::new(status_msg)
188            } else {
189                status
190            };
191
192            #[cfg(feature = "unstable-msc4532")]
193            let status_msg = status.msg.clone();
194
195            #[cfg(feature = "unstable-msc4532")]
196            let currently_active = if presence.is_msc4532_state() {
197                // ignore the provided currently_active value and use the proposal's definition
198                Some(presence.currently_active())
199            } else {
200                currently_active
201            };
202
203            #[allow(deprecated)]
204            Ok(Self {
205                #[cfg(feature = "unstable-msc4532")]
206                status,
207                status_msg,
208                currently_active,
209                last_active_ago,
210                presence,
211            })
212        }
213    }
214
215    // Servers serialize the data as-is, clients are expected to handle old fields as needed.
216    impl Serialize for ResponseBody {
217        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
218        where
219            S: Serializer,
220        {
221            #[allow(deprecated)]
222            let ResponseBody {
223                status_msg,
224                #[cfg(feature = "unstable-msc4532")]
225                status,
226                currently_active,
227                last_active_ago,
228                presence,
229            } = self.clone();
230
231            ResponseBodyRepr {
232                status_msg,
233                #[cfg(feature = "unstable-msc4532")]
234                status,
235                currently_active,
236                last_active_ago,
237                presence,
238            }
239            .serialize(serializer)
240        }
241    }
242}