whatsapp_rust/features/
contacts.rs1use crate::client::Client;
7use crate::request::IqError;
8use anyhow::Result;
9use log::debug;
10use std::collections::HashMap;
11use wacore::iq::contacts::{ProfilePictureSpec, ProfilePictureType};
12use wacore::iq::usync::{ContactInfoSpec, IsOnWhatsAppSpec, UserInfoSpec};
13use wacore_binary::jid::{Jid, JidExt};
14
15pub use wacore::iq::contacts::ProfilePicture;
17pub use wacore::iq::usync::{ContactInfo, IsOnWhatsAppResult, UserInfo};
18
19pub struct Contacts<'a> {
20 client: &'a Client,
21}
22
23impl<'a> Contacts<'a> {
24 pub(crate) fn new(client: &'a Client) -> Self {
25 Self { client }
26 }
27
28 pub async fn is_on_whatsapp(&self, phones: &[&str]) -> Result<Vec<IsOnWhatsAppResult>> {
29 if phones.is_empty() {
30 return Ok(Vec::new());
31 }
32
33 debug!("is_on_whatsapp: checking {} numbers", phones.len());
34
35 let request_id = self.client.generate_request_id();
36 let phone_strings: Vec<String> = phones.iter().map(|s| s.to_string()).collect();
37 let spec = IsOnWhatsAppSpec::new(phone_strings, request_id);
38
39 Ok(self.client.execute(spec).await?)
40 }
41
42 pub async fn get_info(&self, phones: &[&str]) -> Result<Vec<ContactInfo>> {
43 if phones.is_empty() {
44 return Ok(Vec::new());
45 }
46
47 debug!("get_info: fetching info for {} numbers", phones.len());
48
49 let request_id = self.client.generate_request_id();
50 let phone_strings: Vec<String> = phones.iter().map(|s| s.to_string()).collect();
51 let spec = ContactInfoSpec::new(phone_strings, request_id);
52
53 Ok(self.client.execute(spec).await?)
54 }
55
56 pub async fn get_profile_picture(
57 &self,
58 jid: &Jid,
59 preview: bool,
60 ) -> Result<Option<ProfilePicture>> {
61 debug!(
62 "get_profile_picture: fetching {} picture for {}",
63 if preview { "preview" } else { "full" },
64 jid
65 );
66
67 let picture_type = if preview {
68 ProfilePictureType::Preview
69 } else {
70 ProfilePictureType::Full
71 };
72 let mut spec = ProfilePictureSpec::new(jid, picture_type);
73
74 if !jid.is_group()
76 && !jid.is_newsletter()
77 && let Some(token) = self.client.lookup_tc_token_for_jid(jid).await
78 {
79 spec = spec.with_tc_token(token);
80 }
81
82 match self.client.execute(spec).await {
83 Ok(pic) => Ok(pic),
84 Err(IqError::ServerError { code, .. }) if code == 404 || code == 401 => Ok(None),
87 Err(e) => Err(e.into()),
88 }
89 }
90
91 pub async fn get_user_info(&self, jids: &[Jid]) -> Result<HashMap<Jid, UserInfo>> {
92 if jids.is_empty() {
93 return Ok(HashMap::new());
94 }
95
96 debug!("get_user_info: fetching info for {} JIDs", jids.len());
97
98 let request_id = self.client.generate_request_id();
99 let spec = UserInfoSpec::new(jids.to_vec(), request_id);
100
101 Ok(self.client.execute(spec).await?)
102 }
103}
104
105impl Client {
106 pub fn contacts(&self) -> Contacts<'_> {
107 Contacts::new(self)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_contact_info_struct() {
117 let jid: Jid = "1234567890@s.whatsapp.net"
118 .parse()
119 .expect("test JID should be valid");
120 let lid: Jid = "12345678@lid".parse().expect("test JID should be valid");
121
122 let info = ContactInfo {
123 jid: jid.clone(),
124 lid: Some(lid.clone()),
125 is_registered: true,
126 is_business: false,
127 status: Some("Hey there!".to_string()),
128 picture_id: Some(123456789),
129 };
130
131 assert!(info.is_registered);
132 assert!(!info.is_business);
133 assert_eq!(info.status, Some("Hey there!".to_string()));
134 assert_eq!(info.picture_id, Some(123456789));
135 assert!(info.lid.is_some());
136 }
137
138 #[test]
139 fn test_profile_picture_struct() {
140 let pic = ProfilePicture {
141 id: "123456789".to_string(),
142 url: "https://example.com/pic.jpg".to_string(),
143 direct_path: Some("/v/pic.jpg".to_string()),
144 hash: None,
145 };
146
147 assert_eq!(pic.id, "123456789");
148 assert_eq!(pic.url, "https://example.com/pic.jpg");
149 assert!(pic.direct_path.is_some());
150 }
151
152 #[test]
153 fn test_is_on_whatsapp_result_struct() {
154 let jid: Jid = "1234567890@s.whatsapp.net"
155 .parse()
156 .expect("test JID should be valid");
157 let result = IsOnWhatsAppResult {
158 jid,
159 is_registered: true,
160 };
161
162 assert!(result.is_registered);
163 }
164}