whatsapp_rust/features/
blocking.rs1use crate::client::Client;
2use crate::jid_utils::server_jid;
3use crate::request::{InfoQuery, IqError};
4use anyhow::Result;
5use log::debug;
6use wacore_binary::builder::NodeBuilder;
7use wacore_binary::jid::Jid;
8use wacore_binary::node::NodeContent;
9
10#[derive(Debug, Clone)]
11pub struct BlocklistEntry {
12 pub jid: Jid,
13 pub timestamp: Option<u64>,
14}
15
16pub struct Blocking<'a> {
17 client: &'a Client,
18}
19
20impl<'a> Blocking<'a> {
21 pub(crate) fn new(client: &'a Client) -> Self {
22 Self { client }
23 }
24
25 pub async fn block(&self, jid: &Jid) -> Result<(), IqError> {
26 debug!(target: "Blocking", "Blocking contact: {}", jid);
27 self.update_blocklist(jid, "block").await
28 }
29
30 pub async fn unblock(&self, jid: &Jid) -> Result<(), IqError> {
31 debug!(target: "Blocking", "Unblocking contact: {}", jid);
32 self.update_blocklist(jid, "unblock").await
33 }
34
35 pub async fn get_blocklist(&self) -> Result<Vec<BlocklistEntry>> {
36 debug!(target: "Blocking", "Fetching blocklist...");
37
38 let iq = InfoQuery::get("blocklist", server_jid(), None);
39
40 let response = self.client.send_iq(iq).await?;
41 self.parse_blocklist_response(&response)
42 }
43
44 async fn update_blocklist(&self, jid: &Jid, action: &str) -> Result<(), IqError> {
45 let item_node = NodeBuilder::new("item")
46 .attr("action", action)
47 .attr("jid", jid.to_string())
48 .build();
49
50 let iq = InfoQuery::set(
51 "blocklist",
52 server_jid(),
53 Some(NodeContent::Nodes(vec![item_node])),
54 );
55
56 self.client.send_iq(iq).await?;
57 debug!(target: "Blocking", "Successfully {}ed contact: {}", action, jid);
58 Ok(())
59 }
60
61 fn parse_blocklist_response(
62 &self,
63 node: &wacore_binary::node::Node,
64 ) -> Result<Vec<BlocklistEntry>> {
65 let mut entries = Vec::new();
66
67 let items = if let Some(list) = node.get_optional_child("list") {
68 list.get_children_by_tag("item")
69 } else {
70 node.get_children_by_tag("item")
71 };
72
73 for item in items {
74 if let Some(jid_str) = item.attrs().optional_string("jid")
75 && let Ok(jid) = jid_str.parse::<Jid>()
76 {
77 let timestamp = item.attrs().optional_u64("t");
78 entries.push(BlocklistEntry { jid, timestamp });
79 }
80 }
81
82 debug!(target: "Blocking", "Parsed {} blocked contacts", entries.len());
83 Ok(entries)
84 }
85
86 pub async fn is_blocked(&self, jid: &Jid) -> Result<bool> {
87 let blocklist = self.get_blocklist().await?;
88 Ok(blocklist.iter().any(|e| e.jid.user == jid.user))
89 }
90}
91
92impl Client {
93 pub fn blocking(&self) -> Blocking<'_> {
94 Blocking::new(self)
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_blocklist_entry() {
104 let jid: Jid = "1234567890@s.whatsapp.net"
105 .parse()
106 .expect("test JID should be valid");
107 let entry = BlocklistEntry {
108 jid: jid.clone(),
109 timestamp: Some(1234567890),
110 };
111
112 assert_eq!(entry.jid.user, "1234567890");
113 assert_eq!(entry.timestamp, Some(1234567890));
114 }
115}