pub struct Page { /* private fields */ }Implementations§
Source§impl Page
impl Page
Navigate to URL (returns immediately, does not wait for load).
Examples found in repository?
examples/game_test.rs (line 84)
30async fn main() {
31 load_dotenv();
32 println!("=== Game Test + WS Detection ===\n");
33
34 // 1. Fetch game URL
35 println!("Fetching game URL...");
36 let client = reqwest::Client::new();
37 let resp = client
38 .post(api_url())
39 .header("Authorization", format!("Bearer {}", api_token()))
40 .header("Content-Type", "application/json")
41 .header("Accept", "application/json")
42 .body(r#"{"user_id": "beezsbee"}"#)
43 .send()
44 .await
45 .expect("API request failed");
46
47 let body: serde_json::Value = resp.json().await.expect("JSON parse failed");
48 let game_url = body["url"].as_str().expect("no 'url' in response");
49 println!("Game URL: {game_url}\n");
50
51 // 2. Launch browser
52 let browser = Browser::builder()
53 .headful()
54 .profile(42, 12345)
55 .build()
56 .await
57 .expect("browser launch failed");
58
59 // 3. Open blank page first so we can set up listeners before navigation
60 let page = browser.new_page("about:blank").await.expect("page failed");
61
62 // Enable network domain for WS events
63 page.cdp().execute(EnableParams::default()).await.expect("network enable failed");
64
65 // Set up WS event listeners
66 let mut ws_created = page.cdp().event_listener::<EventWebSocketCreated>().await.expect("listener failed");
67 let mut ws_closed = page.cdp().event_listener::<EventWebSocketClosed>().await.expect("listener failed");
68
69 // Spawn listener tasks
70 tokio::spawn(async move {
71 while let Some(ev) = ws_created.next().await {
72 println!("[WS CREATED] request_id={:?} url={}", ev.request_id, ev.url);
73 }
74 });
75
76 tokio::spawn(async move {
77 while let Some(ev) = ws_closed.next().await {
78 println!("[WS CLOSED] request_id={:?}", ev.request_id);
79 }
80 });
81
82 // 4. Navigate to game (human simulation runs automatically)
83 println!("Navigating to game...");
84 page.navigate(game_url).await.expect("nav failed");
85
86 // 5. Wait and monitor
87 println!("Watching for WebSocket connections...\n");
88 loop {
89 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
90 }
91}Sourcepub async fn goto(&self, url: &str) -> Result<()>
pub async fn goto(&self, url: &str) -> Result<()>
Navigate to URL and wait for page load.
Examples found in repository?
examples/tls_check.rs (line 36)
21async fn main() {
22 load_dotenv();
23 println!("=== TLS/HTTP2 Fingerprint Check ===\n");
24
25 let browser = Browser::builder()
26 .headful()
27 .random()
28 .build()
29 .await
30 .expect("browser launch failed");
31
32 let page = browser.new_page("about:blank").await.expect("page failed");
33
34 // 1. Check TLS fingerprint
35 println!("--- tls.peet.ws ---");
36 page.goto("https://tls.peet.ws/api/all").await.expect("nav failed");
37 tokio::time::sleep(std::time::Duration::from_secs(3)).await;
38
39 let body = page.js("document.body.innerText").await.unwrap_or_default();
40 if let Ok(v) = serde_json::from_str::<serde_json::Value>(&body) {
41 // TLS
42 if let Some(tls) = v.get("tls") {
43 println!("JA3 hash: {}", tls.get("ja3_hash").and_then(|v| v.as_str()).unwrap_or("N/A"));
44 println!("JA3: {}", tls.get("ja3").and_then(|v| v.as_str()).unwrap_or("N/A"));
45 println!("JA4: {}", tls.get("ja4").and_then(|v| v.as_str()).unwrap_or("N/A"));
46 println!("TLS version: {}", tls.get("version").and_then(|v| v.as_str()).unwrap_or("N/A"));
47 }
48
49 // HTTP2
50 println!();
51 if let Some(h2) = v.get("http2") {
52 println!("HTTP2 fingerprint: {}", h2.get("akamai_fingerprint").and_then(|v| v.as_str()).unwrap_or("N/A"));
53 println!("Pseudo-header order: {}", h2.get("sent_pseudo_header_fields_order").unwrap_or(&serde_json::Value::Null));
54 if let Some(settings) = h2.get("sent_settings") {
55 println!("SETTINGS: {}", settings);
56 }
57 }
58
59 // HTTP version used
60 println!();
61 if let Some(ip) = v.get("ip") {
62 println!("Client IP: {}", ip.as_str().unwrap_or("N/A"));
63 }
64 if let Some(http_version) = v.get("http_version") {
65 println!("HTTP version: {}", http_version.as_str().unwrap_or("N/A"));
66 }
67
68 // User-Agent
69 println!();
70 if let Some(h1) = v.get("http1") {
71 if let Some(headers) = h1.get("headers") {
72 if let Some(arr) = headers.as_array() {
73 for h in arr {
74 let name = h.get("name").and_then(|n| n.as_str()).unwrap_or("");
75 let val = h.get("value").and_then(|n| n.as_str()).unwrap_or("");
76 if name.to_lowercase() == "user-agent" {
77 println!("User-Agent: {}", val);
78 }
79 }
80 }
81 }
82 }
83 } else {
84 println!("Failed to parse response: {}", &body[..body.len().min(500)]);
85 }
86
87 // 2. Check known Chrome 134 reference values
88 println!("\n--- Expected Chrome 134 stock values ---");
89 println!("JA3 hash: cd49876058a26ccf43e241ccc4e4b5d3 (Chrome 134 Windows)");
90 println!("JA4: t13d1517h2_8daaf6152771_02713d6af8 (Chrome 134 Windows)");
91 println!("HTTP2 Akamai: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p");
92
93 println!("\nBrowser still open. Press Ctrl+C to exit.");
94 loop {
95 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
96 }
97}More examples
examples/batch_test.rs (line 213)
110async fn main() {
111 load_dotenv();
112 let total = PER_ROUND * ROUNDS;
113 println!("=== Proxy batch: {}×{} = {} | live {}s | rotate {}s cooldown ===\n",
114 PER_ROUND, ROUNDS, total, BROWSER_LIVE_SECS, ROTATE_COOLDOWN_SECS);
115
116 let start = Instant::now();
117 let mut ok = 0u32;
118 let mut fail = 0u32;
119
120 for round in 0..ROUNDS {
121 println!("--- Round {}/{} ---", round + 1, ROUNDS);
122
123 // 1. Rotate IP and get proxy
124 let proxy = match rotate_and_get_proxy().await {
125 Some(p) => p,
126 None => {
127 println!(" Skip round — no proxy");
128 fail += PER_ROUND as u32;
129 continue;
130 }
131 };
132
133 // 2. Verify proxy works
134 if !verify_proxy(&proxy).await {
135 println!(" Skip round — proxy dead");
136 fail += PER_ROUND as u32;
137 continue;
138 }
139
140 // 3. Launch 3 browsers in parallel
141 let offset = round * PER_ROUND;
142 let mut handles = Vec::new();
143
144 for i in 0..PER_ROUND {
145 let idx = offset + i;
146 let user_id = USERS[idx % USERS.len()].to_string();
147 let proxy = proxy.clone();
148 let api = api_url();
149 let token = api_token();
150
151 handles.push(tokio::spawn(async move {
152 let label = format!("[{:02}/{}]", idx + 1, total);
153 let t = Instant::now();
154
155 // Get game URL (direct, no proxy)
156 let game_url = match reqwest::Client::new()
157 .post(&api)
158 .header("Authorization", format!("Bearer {}", token))
159 .header("Content-Type", "application/json")
160 .body(format!(r#"{{"user_id": "{}"}}"#, user_id))
161 .send().await
162 {
163 Ok(resp) => {
164 let body: serde_json::Value = resp.json().await.unwrap_or_default();
165 match body["url"].as_str() {
166 Some(u) if !u.is_empty() => u.to_string(),
167 _ => return format!("{label} FAIL api: {body}"),
168 }
169 }
170 Err(e) => return format!("{label} FAIL api: {e}"),
171 };
172 let api_ms = t.elapsed().as_millis();
173
174 // Launch browser with proxy
175 let browser = match Browser::builder()
176 .headful()
177 .random()
178 .proxy(&proxy)
179 .build().await
180 {
181 Ok(b) => b,
182 Err(e) => return format!("{label} FAIL launch: {e}"),
183 };
184 let launch_ms = t.elapsed().as_millis() - api_ms;
185
186 // Navigate (human simulation auto-starts with aggressive scroll)
187 let page = match browser.new_page("about:blank").await {
188 Ok(p) => p,
189 Err(e) => return format!("{label} FAIL page: {e}"),
190 };
191
192 // Aggressive scrolling in background
193 let scroll_page = page.cdp().clone();
194 let scroll_handle = tokio::spawn(async move {
195 use chromiumoxide::cdp::browser_protocol::input::{
196 DispatchMouseEventParams, DispatchMouseEventType,
197 };
198 let mut y = 300.0f64;
199 loop {
200 // Scroll down aggressively
201 let mut ev = DispatchMouseEventParams::new(
202 DispatchMouseEventType::MouseWheel, 500.0, y,
203 );
204 ev.delta_x = Some(0.0);
205 ev.delta_y = Some(300.0);
206 let _ = scroll_page.execute(ev).await;
207 y = ((y + 50.0) % 800.0) + 100.0;
208 tokio::time::sleep(std::time::Duration::from_millis(800)).await;
209 }
210 });
211
212 let t_nav = Instant::now();
213 if let Err(e) = page.goto(&game_url).await {
214 scroll_handle.abort();
215 return format!("{label} FAIL nav: {e}");
216 }
217 let nav_ms = t_nav.elapsed().as_millis();
218
219 // Wait for cookies
220 tokio::time::sleep(std::time::Duration::from_secs(5)).await;
221
222 let cookies = page.cdp().get_cookies().await.unwrap_or_default();
223 let has_evo = cookies.iter().any(|c| c.name == "EVOSESSIONID");
224 let evo = cookies.iter().find(|c| c.name == "EVOSESSIONID")
225 .map(|c| c.value.clone())
226 .unwrap_or_else(|| "N/A".into());
227 let cookie_names: Vec<String> = cookies.iter().map(|c| c.name.clone()).collect();
228
229 // Stay alive for remaining time (human sim + aggressive scroll running)
230 let elapsed = t.elapsed().as_secs();
231 if elapsed < BROWSER_LIVE_SECS {
232 tokio::time::sleep(std::time::Duration::from_secs(BROWSER_LIVE_SECS - elapsed)).await;
233 }
234
235 // Close browser
236 scroll_handle.abort();
237 let _ = browser.close().await;
238 let total_ms = t.elapsed().as_millis();
239
240 let status = if has_evo { "OK" } else { "MISS" };
241 format!("{label} {status} | {user_id:<18} | total:{total_ms}ms api:{api_ms}ms launch:{launch_ms}ms nav:{nav_ms}ms | cookies:{} | EVO:{evo}\n {:?}",
242 cookies.len(), cookie_names)
243 }));
244 }
245
246 for h in handles {
247 match h.await {
248 Ok(msg) => {
249 if msg.contains("] OK ") { ok += 1; } else { fail += 1; }
250 println!("{msg}");
251 }
252 Err(e) => { println!(" task panic: {e}"); fail += 1; }
253 }
254 }
255
256 println!();
257 }
258
259 let elapsed = start.elapsed().as_secs_f64();
260 println!("=== OK:{ok} FAIL:{fail} | {elapsed:.0}s total | {:.1}s/profile ===",
261 elapsed / total as f64);
262}Sourcepub async fn wait_for_load(&self) -> Result<()>
pub async fn wait_for_load(&self) -> Result<()>
Wait for next navigation/load event.
Sourcepub async fn js(&self, expr: &str) -> Result<String>
pub async fn js(&self, expr: &str) -> Result<String>
Run JS expression in page context, return result as String.
Examples found in repository?
examples/youtube_test.rs (line 17)
4async fn main() {
5 println!("=== YouTube Test ===\n");
6
7 let browser = Browser::builder()
8 .headful()
9 .profile(7, 777)
10 .build()
11 .await
12 .expect("launch failed");
13
14 let page = browser.new_page("https://www.youtube.com").await.expect("nav failed");
15 page.wait(3000).await;
16
17 let title = page.js("document.title").await.unwrap_or_default();
18 let url = page.url().await.unwrap_or_default();
19 println!("Title: {title}");
20 println!("URL: {url}");
21
22 println!("\nBrowser open. Ctrl+C to exit.");
23 loop {
24 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
25 }
26}More examples
examples/smoke_test.rs (line 24)
4async fn main() {
5 println!("=== Smoke Test ===\n");
6
7 let browser = Browser::builder()
8 .headful()
9 .profile(7, 777)
10 .build()
11 .await
12 .expect("launch failed");
13
14 let page = browser.new_page("about:blank").await.expect("page failed");
15
16 let checks = &[
17 ("webdriver", "navigator.webdriver.toString()"),
18 ("timezone", "Intl.DateTimeFormat().resolvedOptions().timeZone"),
19 ("cores", "navigator.hardwareConcurrency.toString()"),
20 ("screen", "screen.width+'x'+screen.height"),
21 ("UA", "navigator.userAgent"),
22 ];
23 for (name, expr) in checks {
24 let val = page.js(expr).await.unwrap_or_default();
25 println!("{name}: {val}");
26 }
27
28 println!("\nBrowser open. Ctrl+C to exit.");
29 loop {
30 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
31 }
32}examples/tls_check.rs (line 39)
21async fn main() {
22 load_dotenv();
23 println!("=== TLS/HTTP2 Fingerprint Check ===\n");
24
25 let browser = Browser::builder()
26 .headful()
27 .random()
28 .build()
29 .await
30 .expect("browser launch failed");
31
32 let page = browser.new_page("about:blank").await.expect("page failed");
33
34 // 1. Check TLS fingerprint
35 println!("--- tls.peet.ws ---");
36 page.goto("https://tls.peet.ws/api/all").await.expect("nav failed");
37 tokio::time::sleep(std::time::Duration::from_secs(3)).await;
38
39 let body = page.js("document.body.innerText").await.unwrap_or_default();
40 if let Ok(v) = serde_json::from_str::<serde_json::Value>(&body) {
41 // TLS
42 if let Some(tls) = v.get("tls") {
43 println!("JA3 hash: {}", tls.get("ja3_hash").and_then(|v| v.as_str()).unwrap_or("N/A"));
44 println!("JA3: {}", tls.get("ja3").and_then(|v| v.as_str()).unwrap_or("N/A"));
45 println!("JA4: {}", tls.get("ja4").and_then(|v| v.as_str()).unwrap_or("N/A"));
46 println!("TLS version: {}", tls.get("version").and_then(|v| v.as_str()).unwrap_or("N/A"));
47 }
48
49 // HTTP2
50 println!();
51 if let Some(h2) = v.get("http2") {
52 println!("HTTP2 fingerprint: {}", h2.get("akamai_fingerprint").and_then(|v| v.as_str()).unwrap_or("N/A"));
53 println!("Pseudo-header order: {}", h2.get("sent_pseudo_header_fields_order").unwrap_or(&serde_json::Value::Null));
54 if let Some(settings) = h2.get("sent_settings") {
55 println!("SETTINGS: {}", settings);
56 }
57 }
58
59 // HTTP version used
60 println!();
61 if let Some(ip) = v.get("ip") {
62 println!("Client IP: {}", ip.as_str().unwrap_or("N/A"));
63 }
64 if let Some(http_version) = v.get("http_version") {
65 println!("HTTP version: {}", http_version.as_str().unwrap_or("N/A"));
66 }
67
68 // User-Agent
69 println!();
70 if let Some(h1) = v.get("http1") {
71 if let Some(headers) = h1.get("headers") {
72 if let Some(arr) = headers.as_array() {
73 for h in arr {
74 let name = h.get("name").and_then(|n| n.as_str()).unwrap_or("");
75 let val = h.get("value").and_then(|n| n.as_str()).unwrap_or("");
76 if name.to_lowercase() == "user-agent" {
77 println!("User-Agent: {}", val);
78 }
79 }
80 }
81 }
82 }
83 } else {
84 println!("Failed to parse response: {}", &body[..body.len().min(500)]);
85 }
86
87 // 2. Check known Chrome 134 reference values
88 println!("\n--- Expected Chrome 134 stock values ---");
89 println!("JA3 hash: cd49876058a26ccf43e241ccc4e4b5d3 (Chrome 134 Windows)");
90 println!("JA4: t13d1517h2_8daaf6152771_02713d6af8 (Chrome 134 Windows)");
91 println!("HTTP2 Akamai: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p");
92
93 println!("\nBrowser still open. Press Ctrl+C to exit.");
94 loop {
95 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
96 }
97}Sourcepub async fn js_as<T: DeserializeOwned>(&self, expr: &str) -> Result<T>
pub async fn js_as<T: DeserializeOwned>(&self, expr: &str) -> Result<T>
Run JS and deserialize result into T.
Sourcepub async fn js_on_new_document(&self, script: &str) -> Result<()>
pub async fn js_on_new_document(&self, script: &str) -> Result<()>
Inject script that runs on every new document before page JS.
Sourcepub async fn url(&self) -> Result<String>
pub async fn url(&self) -> Result<String>
Current page URL.
Examples found in repository?
examples/youtube_test.rs (line 18)
4async fn main() {
5 println!("=== YouTube Test ===\n");
6
7 let browser = Browser::builder()
8 .headful()
9 .profile(7, 777)
10 .build()
11 .await
12 .expect("launch failed");
13
14 let page = browser.new_page("https://www.youtube.com").await.expect("nav failed");
15 page.wait(3000).await;
16
17 let title = page.js("document.title").await.unwrap_or_default();
18 let url = page.url().await.unwrap_or_default();
19 println!("Title: {title}");
20 println!("URL: {url}");
21
22 println!("\nBrowser open. Ctrl+C to exit.");
23 loop {
24 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
25 }
26}Sourcepub async fn screenshot(&self) -> Result<Vec<u8>>
pub async fn screenshot(&self) -> Result<Vec<u8>>
Capture PNG screenshot.
Sourcepub async fn click(&self, x: f64, y: f64) -> Result<()>
pub async fn click(&self, x: f64, y: f64) -> Result<()>
Click at (x, y) with realistic mouse movement + timing.
Sourcepub async fn type_text(&self, text: &str) -> Result<()>
pub async fn type_text(&self, text: &str) -> Result<()>
Type text into focused element with realistic keystroke timing.
Sourcepub async fn scroll(&self, delta_y: f64) -> Result<()>
pub async fn scroll(&self, delta_y: f64) -> Result<()>
Scroll by delta_y pixels (positive = down).
Sourcepub async fn wait(&self, ms: u64)
pub async fn wait(&self, ms: u64)
Sleep for ms milliseconds.
Examples found in repository?
examples/youtube_test.rs (line 15)
4async fn main() {
5 println!("=== YouTube Test ===\n");
6
7 let browser = Browser::builder()
8 .headful()
9 .profile(7, 777)
10 .build()
11 .await
12 .expect("launch failed");
13
14 let page = browser.new_page("https://www.youtube.com").await.expect("nav failed");
15 page.wait(3000).await;
16
17 let title = page.js("document.title").await.unwrap_or_default();
18 let url = page.url().await.unwrap_or_default();
19 println!("Title: {title}");
20 println!("URL: {url}");
21
22 println!("\nBrowser open. Ctrl+C to exit.");
23 loop {
24 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
25 }
26}Sourcepub fn cdp(&self) -> &CdpPage
pub fn cdp(&self) -> &CdpPage
Access underlying chromiumoxide Page for raw CDP.
Examples found in repository?
examples/game_test.rs (line 63)
30async fn main() {
31 load_dotenv();
32 println!("=== Game Test + WS Detection ===\n");
33
34 // 1. Fetch game URL
35 println!("Fetching game URL...");
36 let client = reqwest::Client::new();
37 let resp = client
38 .post(api_url())
39 .header("Authorization", format!("Bearer {}", api_token()))
40 .header("Content-Type", "application/json")
41 .header("Accept", "application/json")
42 .body(r#"{"user_id": "beezsbee"}"#)
43 .send()
44 .await
45 .expect("API request failed");
46
47 let body: serde_json::Value = resp.json().await.expect("JSON parse failed");
48 let game_url = body["url"].as_str().expect("no 'url' in response");
49 println!("Game URL: {game_url}\n");
50
51 // 2. Launch browser
52 let browser = Browser::builder()
53 .headful()
54 .profile(42, 12345)
55 .build()
56 .await
57 .expect("browser launch failed");
58
59 // 3. Open blank page first so we can set up listeners before navigation
60 let page = browser.new_page("about:blank").await.expect("page failed");
61
62 // Enable network domain for WS events
63 page.cdp().execute(EnableParams::default()).await.expect("network enable failed");
64
65 // Set up WS event listeners
66 let mut ws_created = page.cdp().event_listener::<EventWebSocketCreated>().await.expect("listener failed");
67 let mut ws_closed = page.cdp().event_listener::<EventWebSocketClosed>().await.expect("listener failed");
68
69 // Spawn listener tasks
70 tokio::spawn(async move {
71 while let Some(ev) = ws_created.next().await {
72 println!("[WS CREATED] request_id={:?} url={}", ev.request_id, ev.url);
73 }
74 });
75
76 tokio::spawn(async move {
77 while let Some(ev) = ws_closed.next().await {
78 println!("[WS CLOSED] request_id={:?}", ev.request_id);
79 }
80 });
81
82 // 4. Navigate to game (human simulation runs automatically)
83 println!("Navigating to game...");
84 page.navigate(game_url).await.expect("nav failed");
85
86 // 5. Wait and monitor
87 println!("Watching for WebSocket connections...\n");
88 loop {
89 tokio::time::sleep(std::time::Duration::from_secs(60)).await;
90 }
91}More examples
examples/batch_test.rs (line 193)
110async fn main() {
111 load_dotenv();
112 let total = PER_ROUND * ROUNDS;
113 println!("=== Proxy batch: {}×{} = {} | live {}s | rotate {}s cooldown ===\n",
114 PER_ROUND, ROUNDS, total, BROWSER_LIVE_SECS, ROTATE_COOLDOWN_SECS);
115
116 let start = Instant::now();
117 let mut ok = 0u32;
118 let mut fail = 0u32;
119
120 for round in 0..ROUNDS {
121 println!("--- Round {}/{} ---", round + 1, ROUNDS);
122
123 // 1. Rotate IP and get proxy
124 let proxy = match rotate_and_get_proxy().await {
125 Some(p) => p,
126 None => {
127 println!(" Skip round — no proxy");
128 fail += PER_ROUND as u32;
129 continue;
130 }
131 };
132
133 // 2. Verify proxy works
134 if !verify_proxy(&proxy).await {
135 println!(" Skip round — proxy dead");
136 fail += PER_ROUND as u32;
137 continue;
138 }
139
140 // 3. Launch 3 browsers in parallel
141 let offset = round * PER_ROUND;
142 let mut handles = Vec::new();
143
144 for i in 0..PER_ROUND {
145 let idx = offset + i;
146 let user_id = USERS[idx % USERS.len()].to_string();
147 let proxy = proxy.clone();
148 let api = api_url();
149 let token = api_token();
150
151 handles.push(tokio::spawn(async move {
152 let label = format!("[{:02}/{}]", idx + 1, total);
153 let t = Instant::now();
154
155 // Get game URL (direct, no proxy)
156 let game_url = match reqwest::Client::new()
157 .post(&api)
158 .header("Authorization", format!("Bearer {}", token))
159 .header("Content-Type", "application/json")
160 .body(format!(r#"{{"user_id": "{}"}}"#, user_id))
161 .send().await
162 {
163 Ok(resp) => {
164 let body: serde_json::Value = resp.json().await.unwrap_or_default();
165 match body["url"].as_str() {
166 Some(u) if !u.is_empty() => u.to_string(),
167 _ => return format!("{label} FAIL api: {body}"),
168 }
169 }
170 Err(e) => return format!("{label} FAIL api: {e}"),
171 };
172 let api_ms = t.elapsed().as_millis();
173
174 // Launch browser with proxy
175 let browser = match Browser::builder()
176 .headful()
177 .random()
178 .proxy(&proxy)
179 .build().await
180 {
181 Ok(b) => b,
182 Err(e) => return format!("{label} FAIL launch: {e}"),
183 };
184 let launch_ms = t.elapsed().as_millis() - api_ms;
185
186 // Navigate (human simulation auto-starts with aggressive scroll)
187 let page = match browser.new_page("about:blank").await {
188 Ok(p) => p,
189 Err(e) => return format!("{label} FAIL page: {e}"),
190 };
191
192 // Aggressive scrolling in background
193 let scroll_page = page.cdp().clone();
194 let scroll_handle = tokio::spawn(async move {
195 use chromiumoxide::cdp::browser_protocol::input::{
196 DispatchMouseEventParams, DispatchMouseEventType,
197 };
198 let mut y = 300.0f64;
199 loop {
200 // Scroll down aggressively
201 let mut ev = DispatchMouseEventParams::new(
202 DispatchMouseEventType::MouseWheel, 500.0, y,
203 );
204 ev.delta_x = Some(0.0);
205 ev.delta_y = Some(300.0);
206 let _ = scroll_page.execute(ev).await;
207 y = ((y + 50.0) % 800.0) + 100.0;
208 tokio::time::sleep(std::time::Duration::from_millis(800)).await;
209 }
210 });
211
212 let t_nav = Instant::now();
213 if let Err(e) = page.goto(&game_url).await {
214 scroll_handle.abort();
215 return format!("{label} FAIL nav: {e}");
216 }
217 let nav_ms = t_nav.elapsed().as_millis();
218
219 // Wait for cookies
220 tokio::time::sleep(std::time::Duration::from_secs(5)).await;
221
222 let cookies = page.cdp().get_cookies().await.unwrap_or_default();
223 let has_evo = cookies.iter().any(|c| c.name == "EVOSESSIONID");
224 let evo = cookies.iter().find(|c| c.name == "EVOSESSIONID")
225 .map(|c| c.value.clone())
226 .unwrap_or_else(|| "N/A".into());
227 let cookie_names: Vec<String> = cookies.iter().map(|c| c.name.clone()).collect();
228
229 // Stay alive for remaining time (human sim + aggressive scroll running)
230 let elapsed = t.elapsed().as_secs();
231 if elapsed < BROWSER_LIVE_SECS {
232 tokio::time::sleep(std::time::Duration::from_secs(BROWSER_LIVE_SECS - elapsed)).await;
233 }
234
235 // Close browser
236 scroll_handle.abort();
237 let _ = browser.close().await;
238 let total_ms = t.elapsed().as_millis();
239
240 let status = if has_evo { "OK" } else { "MISS" };
241 format!("{label} {status} | {user_id:<18} | total:{total_ms}ms api:{api_ms}ms launch:{launch_ms}ms nav:{nav_ms}ms | cookies:{} | EVO:{evo}\n {:?}",
242 cookies.len(), cookie_names)
243 }));
244 }
245
246 for h in handles {
247 match h.await {
248 Ok(msg) => {
249 if msg.contains("] OK ") { ok += 1; } else { fail += 1; }
250 println!("{msg}");
251 }
252 Err(e) => { println!(" task panic: {e}"); fail += 1; }
253 }
254 }
255
256 println!();
257 }
258
259 let elapsed = start.elapsed().as_secs_f64();
260 println!("=== OK:{ok} FAIL:{fail} | {elapsed:.0}s total | {:.1}s/profile ===",
261 elapsed / total as f64);
262}Auto Trait Implementations§
impl Freeze for Page
impl !RefUnwindSafe for Page
impl Send for Page
impl Sync for Page
impl Unpin for Page
impl UnsafeUnpin for Page
impl !UnwindSafe for Page
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more