use std::time::Duration;
use reqwest::Client;
use serde::Deserialize;
const INFO_TIMEOUT: Duration = Duration::from_secs(4);
const MAX_LEN: usize = 64;
#[derive(Debug, Deserialize)]
struct IpInfo {
#[serde(rename = "processedString")]
processed: Option<String>,
}
#[must_use]
pub fn info_url(ping_url: &str) -> String {
let base = ping_url
.split('?')
.next()
.unwrap_or(ping_url)
.rsplit_once('/')
.map_or("", |(base, _)| base);
format!("{base}/getIP.php?isp=true")
}
pub async fn fetch_client_info(client: &Client, ping_url: &str) -> Option<String> {
let url = info_url(ping_url);
let response = tokio::time::timeout(INFO_TIMEOUT, client.get(url).send())
.await
.ok()?
.ok()?;
if !response.status().is_success() {
return None;
}
let body = tokio::time::timeout(INFO_TIMEOUT, response.text())
.await
.ok()?
.ok()?;
let text = serde_json::from_str::<IpInfo>(&body)
.ok()
.and_then(|info| info.processed)
.unwrap_or(body);
let cleaned = text.trim();
if cleaned.is_empty() || cleaned.len() > MAX_LEN * 4 {
return None;
}
Some(cleaned.chars().take(MAX_LEN).collect())
}