use crate::identity::{Browser, Identity, Os};
const PROPERTY_MODIFIER_JS: &str = include_str!("../../js/property_modifier.js");
const MAIN_WORLD_JS: &str = include_str!("../../js/main_world.js");
const BROWSER_CHROME_JS: &str = include_str!("../../js/browser_chrome.js");
const BROWSER_SAFARI_JS: &str = include_str!("../../js/browser_safari.js");
const BROWSER_EDGE_JS: &str = include_str!("../../js/browser_edge.js");
pub fn build_stealth_script(identity: &Identity) -> String {
let lang0 = identity
.language
.first()
.map(|s| s.as_str())
.unwrap_or("en-US");
let languages_json =
serde_json::to_string(&identity.language).unwrap_or_else(|_| r#"["en-US"]"#.into());
let max_touch = if identity.has_touch { 5 } else { 0 };
let avail_height = identity.screen.logical_height.saturating_sub(40);
let charging = !identity.has_battery || identity.has_mouse;
let charging_time = if identity.has_battery { "Infinity" } else { "0" };
let discharging_time = if identity.has_battery { "7200" } else { "Infinity" };
let battery_percentage: u8 = rand::random_range(20..=100);
let history_block = match identity.history_count {
Some(count) => format!(
r#"(function() {{
var historyCount = {};
for (var n = 0; n < historyCount; ++n) {{
if (window.history.length >= historyCount) break;
window.history.pushState(null, '');
}}
}})()"#,
count
),
None => String::new(),
};
let is_ios = matches!(identity.os, Os::Ios);
let browser_block = if is_ios {
BROWSER_SAFARI_JS.to_string()
} else {
match identity.browser {
Browser::Chrome => BROWSER_CHROME_JS.to_string(),
Browser::Safari => BROWSER_SAFARI_JS.to_string(),
Browser::Edge => BROWSER_EDGE_JS.to_string(),
}
};
let script = MAIN_WORLD_JS
.replace("{{NAVIGATOR_PLATFORM}}", identity.platform.navigator_platform.as_str())
.replace("{{HARDWARE_CONCURRENCY}}", &identity.hardware_concurrency.to_string())
.replace("{{MEMORY}}", &identity.memory.to_string())
.replace("{{LANGUAGES_JSON}}", &languages_json)
.replace("{{LANGUAGE_0}}", lang0)
.replace("{{MAX_TOUCH_POINTS}}", &max_touch.to_string())
.replace("{{LOGICAL_WIDTH}}", &identity.screen.logical_width.to_string())
.replace("{{AVAIL_HEIGHT}}", &avail_height.to_string())
.replace("{{WEBGL_VENDOR}}", &escape_js(&identity.gpu.webgl_vendor))
.replace("{{WEBGL_RENDERER}}", &escape_js(&identity.gpu.webgl_renderer))
.replace("{{HISTORY_BLOCK}}", &history_block)
.replace("{{CHARGING}}", &charging.to_string())
.replace("{{CHARGING_TIME}}", charging_time)
.replace("{{DISCHARGING_TIME}}", discharging_time)
.replace("{{BATTERY_PERCENTAGE}}", &battery_percentage.to_string())
.replace("{{BROWSER_BLOCK}}", &browser_block);
format!("{}\n{}", PROPERTY_MODIFIER_JS, script)
}
fn escape_js(s: &str) -> String {
s.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
}