use regex::Regex;
use super::MqttConfig;
use crate::error::MessengerError;
fn extract_first(html: &str, patterns: &[&str]) -> Result<Option<String>, MessengerError> {
for pattern in patterns {
let regex = Regex::new(pattern)?;
if let Some(value) = regex
.captures(html)
.and_then(|captures| captures.get(1).map(|capture| capture.as_str().to_string()))
{
return Ok(Some(value));
}
}
Ok(None)
}
pub(super) fn extract_fb_dtsg(html: &str) -> Result<Option<String>, MessengerError> {
extract_first(
html,
&[
r#""DTSGInitialData".*?"token":"([^"]+)""#,
r#""DTSGInitData"(?:\s*,\s*\[\])?(?:\s*,\s*)\{[^}]*"token"\s*:\s*"([^"]+)""#,
r#""fb_dtsg":\{"value":"([^"]+)""#,
r#"name="fb_dtsg"\s+value="([^"]+)""#,
],
)
}
pub(super) fn extract_lsd(html: &str) -> Result<Option<String>, MessengerError> {
extract_first(
html,
&[
r#""LSD"\s*,\s*\[\s*\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)""#,
r#""LSD"\s*,\s*\[\s*\]\s*,\s*\{\s*"token"\s*:\s*"([A-Za-z0-9_-]+)""#,
r#""lsd":\{"token":"([^"]+)""#,
r#"name="lsd"\s+value="([^"]+)""#,
],
)
}
pub(super) fn extract_jazoest(html: &str) -> Result<Option<String>, MessengerError> {
if let Some(value) = extract_first(
html,
&[
r#""jazoest":\{"value":"([^"]+)""#,
r#"name="jazoest"\s+value="([^"]+)""#,
],
)? {
return Ok(Some(value));
}
if let Some(fb_dtsg) = extract_fb_dtsg(html)? {
let sum: u32 = fb_dtsg.bytes().map(u32::from).sum();
return Ok(Some(format!("2{sum}")));
}
Ok(None)
}
pub(super) fn extract_mqtt_config(html: &str) -> Option<MqttConfig> {
let client_id = extract_mqtt_client_id(html)?;
let app_id = extract_mqtt_app_id(html)?;
let region = extract_mqtt_region(html)?;
Some(MqttConfig {
client_id,
app_id,
region,
})
}
pub(super) fn extract_client_revision(html: &str) -> Option<u64> {
let patterns = [
r#""client_revision"\s*:\s*(\d+)"?"#,
r#""__spin_r"\s*:\s*(\d+)"?"#,
];
for pattern in patterns {
let regex = Regex::new(pattern).ok()?;
if let Some(value) = regex
.captures(html)
.and_then(|captures| captures.get(1).map(|capture| capture.as_str().to_string()))
.and_then(|value| value.parse::<u64>().ok())
{
return Some(value);
}
}
None
}
fn extract_mqtt_client_id(html: &str) -> Option<String> {
let regex =
Regex::new("\\[\"MqttWebDeviceID\".*?\"clientID\"\\s*:\\s*\"([a-f0-9\\-]+)\"").ok()?;
regex
.captures(html)
.and_then(|captures| captures.get(1).map(|capture| capture.as_str().to_string()))
}
fn extract_mqtt_app_id(html: &str) -> Option<String> {
let regex = Regex::new("\\[\"MqttWebConfig\".*?\"appID\"\\s*:\\s*(\\d+)").ok()?;
regex
.captures(html)
.and_then(|captures| captures.get(1).map(|capture| capture.as_str().to_string()))
}
fn extract_mqtt_region(html: &str) -> Option<String> {
let regex = Regex::new(
"\\[\"MqttWebConfig\".*?\"endpoint\"\\s*:\\s*\"[^\"]*?region=([a-zA-Z0-9_-]+)[^\"]*\"",
)
.ok()?;
regex.captures(html).and_then(|captures| {
captures
.get(1)
.map(|capture| capture.as_str().to_ascii_lowercase())
})
}