whatsapp_rust/features/
presence.rs

1use crate::client::Client;
2use log::{debug, info, warn};
3use wacore_binary::builder::NodeBuilder;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum PresenceStatus {
7    Available,
8    Unavailable,
9}
10
11impl PresenceStatus {
12    fn as_str(&self) -> &'static str {
13        match self {
14            PresenceStatus::Available => "available",
15            PresenceStatus::Unavailable => "unavailable",
16        }
17    }
18}
19
20impl std::fmt::Display for PresenceStatus {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{}", self.as_str())
23    }
24}
25
26impl From<crate::types::presence::Presence> for PresenceStatus {
27    fn from(p: crate::types::presence::Presence) -> Self {
28        match p {
29            crate::types::presence::Presence::Available => PresenceStatus::Available,
30            crate::types::presence::Presence::Unavailable => PresenceStatus::Unavailable,
31        }
32    }
33}
34
35pub struct Presence<'a> {
36    client: &'a Client,
37}
38
39impl<'a> Presence<'a> {
40    pub(crate) fn new(client: &'a Client) -> Self {
41        Self { client }
42    }
43
44    pub async fn set(&self, status: PresenceStatus) -> Result<(), anyhow::Error> {
45        let device_snapshot = self
46            .client
47            .persistence_manager()
48            .get_device_snapshot()
49            .await;
50
51        debug!(
52            "send_presence called with push_name: '{}'",
53            device_snapshot.push_name
54        );
55
56        if device_snapshot.push_name.is_empty() {
57            warn!("Cannot send presence: push_name is empty!");
58            return Err(anyhow::anyhow!(
59                "Cannot send presence without a push name set"
60            ));
61        }
62
63        let presence_type = status.as_str();
64
65        let node = NodeBuilder::new("presence")
66            .attr("type", presence_type)
67            .attr("name", &device_snapshot.push_name)
68            .build();
69
70        info!(
71            "Sending presence stanza: <presence type=\"{}\" name=\"{}\"/>",
72            presence_type,
73            node.attrs.get("name").map_or("", |s| s.as_str())
74        );
75
76        self.client.send_node(node).await.map_err(|e| e.into())
77    }
78
79    pub async fn set_available(&self) -> Result<(), anyhow::Error> {
80        self.set(PresenceStatus::Available).await
81    }
82
83    pub async fn set_unavailable(&self) -> Result<(), anyhow::Error> {
84        self.set(PresenceStatus::Unavailable).await
85    }
86}
87
88impl Client {
89    #[allow(clippy::wrong_self_convention)]
90    pub fn presence(&self) -> Presence<'_> {
91        Presence::new(self)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_presence_status_display() {
101        assert_eq!(PresenceStatus::Available.to_string(), "available");
102        assert_eq!(PresenceStatus::Unavailable.to_string(), "unavailable");
103    }
104
105    #[test]
106    fn test_presence_status_as_str() {
107        assert_eq!(PresenceStatus::Available.as_str(), "available");
108        assert_eq!(PresenceStatus::Unavailable.as_str(), "unavailable");
109    }
110}