whatsapp_rust/client/
lid_pn.rs1use anyhow::Result;
13use log::debug;
14use wacore_binary::jid::Jid;
15
16use super::Client;
17use crate::lid_pn_cache::{LearningSource, LidPnEntry};
18
19impl Client {
20 pub(crate) async fn warm_up_lid_pn_cache(&self) -> Result<(), anyhow::Error> {
24 let backend = self.persistence_manager.backend();
25 let entries = backend.get_all_lid_mappings().await?;
26
27 if entries.is_empty() {
28 debug!("LID-PN cache warm-up: no entries found in storage");
29 return Ok(());
30 }
31
32 let cache_entries: Vec<LidPnEntry> = entries
33 .into_iter()
34 .map(|e| {
35 LidPnEntry::with_timestamp(
36 e.lid,
37 e.phone_number,
38 e.created_at,
39 LearningSource::parse(&e.learning_source),
40 )
41 })
42 .collect();
43
44 self.lid_pn_cache.warm_up(cache_entries).await;
45 Ok(())
46 }
47
48 pub(crate) async fn add_lid_pn_mapping(
52 &self,
53 lid: &str,
54 phone_number: &str,
55 source: LearningSource,
56 ) -> Result<()> {
57 use anyhow::anyhow;
58 use wacore::store::traits::LidPnMappingEntry;
59
60 let is_new_mapping = self
62 .lid_pn_cache
63 .get_current_lid(phone_number)
64 .await
65 .is_none();
66
67 let entry = LidPnEntry::new(lid.to_string(), phone_number.to_string(), source);
69 self.lid_pn_cache.add(entry.clone()).await;
70
71 let backend = self.persistence_manager.backend();
73 let storage_entry = LidPnMappingEntry {
74 lid: entry.lid,
75 phone_number: entry.phone_number,
76 created_at: entry.created_at,
77 updated_at: entry.created_at,
78 learning_source: entry.learning_source.as_str().to_string(),
79 };
80
81 backend
82 .put_lid_mapping(&storage_entry)
83 .await
84 .map_err(|e| anyhow!("persisting LID-PN mapping: {e}"))?;
85
86 if is_new_mapping {
88 self.migrate_device_registry_on_lid_discovery(phone_number, lid)
89 .await;
90 }
91
92 Ok(())
93 }
94
95 pub(crate) async fn resolve_lid_mappings(&self, jids: &[Jid]) -> Vec<Jid> {
102 let mut resolved = Vec::with_capacity(jids.len());
103
104 for jid in jids {
105 if !jid.is_pn() && !jid.is_lid() {
107 resolved.push(jid.clone());
108 continue;
109 }
110
111 if jid.is_lid() {
113 resolved.push(jid.clone());
114 continue;
115 }
116
117 if let Some(lid_user) = self.lid_pn_cache.get_current_lid(&jid.user).await {
119 resolved.push(Jid::lid_device(lid_user, jid.device));
120 } else {
121 resolved.push(jid.clone());
124 }
125 }
126
127 resolved
128 }
129
130 pub(crate) async fn resolve_encryption_jid(&self, target: &Jid) -> Jid {
137 let pn_server = wacore_binary::jid::DEFAULT_USER_SERVER;
138 let lid_server = wacore_binary::jid::HIDDEN_USER_SERVER;
139
140 if target.server == lid_server {
141 target.clone()
143 } else if target.server == pn_server {
144 if let Some(lid_user) = self.lid_pn_cache.get_current_lid(&target.user).await {
146 let lid_jid = Jid {
147 user: lid_user,
148 server: wacore_binary::jid::cow_server_from_str(lid_server),
149 device: target.device,
150 agent: target.agent,
151 integrator: target.integrator,
152 };
153 debug!(
154 "[SEND-LOCK] Resolved {} to LID {} for session lock",
155 target, lid_jid
156 );
157 lid_jid
158 } else {
159 debug!("[SEND-LOCK] No LID mapping for {}, using PN", target);
161 target.clone()
162 }
163 } else {
164 target.clone()
166 }
167 }
168
169 pub async fn get_phone_number_from_lid(&self, lid: &str) -> Option<String> {
180 let lid_user = if lid.contains('@') {
182 lid.split('@').next().unwrap_or(lid)
183 } else {
184 lid
185 };
186 self.lid_pn_cache.get_phone_number(lid_user).await
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193 use crate::lid_pn_cache::LearningSource;
194 use crate::test_utils::create_test_client;
195 use std::sync::Arc;
196 use wacore_binary::jid::HIDDEN_USER_SERVER;
197
198 #[tokio::test]
199 async fn test_resolve_encryption_jid_pn_to_lid() {
200 let client: Arc<Client> = create_test_client().await;
201 let pn = "55999999999";
202 let lid = "100000012345678";
203
204 client
206 .add_lid_pn_mapping(lid, pn, LearningSource::PeerPnMessage)
207 .await
208 .unwrap();
209
210 let pn_jid = Jid::pn(pn);
211 let resolved = client.resolve_encryption_jid(&pn_jid).await;
212
213 assert_eq!(resolved.user, lid);
214 assert_eq!(resolved.server, HIDDEN_USER_SERVER);
215 }
216
217 #[tokio::test]
218 async fn test_resolve_encryption_jid_preserves_lid() {
219 let client: Arc<Client> = create_test_client().await;
220 let lid = "100000012345678";
221 let lid_jid = Jid::lid(lid);
222
223 let resolved = client.resolve_encryption_jid(&lid_jid).await;
224
225 assert_eq!(resolved, lid_jid);
226 }
227
228 #[tokio::test]
229 async fn test_resolve_encryption_jid_no_mapping_returns_pn() {
230 let client: Arc<Client> = create_test_client().await;
231 let pn = "55999999999";
232 let pn_jid = Jid::pn(pn);
233
234 let resolved = client.resolve_encryption_jid(&pn_jid).await;
235
236 assert_eq!(resolved, pn_jid);
237 }
238}