lib/lib.rs
1use reqwest::{Client, Url};
2use scraper::{Html, Selector};
3use std::collections::HashMap;
4use std::error::Error;
5
6pub async fn get_transport_modes() -> Result<Vec<(String, String)>, Box<dyn Error>> {
7 let options_page = reqwest::get("https://bridges.torproject.org/options")
8 .await?
9 .text()
10 .await?;
11
12 let parsed_page = Html::parse_document(&options_page);
13 let select_selector = Selector::parse(r#"select[id="advanced-options-transport"]"#)?;
14
15 let select_options = parsed_page
16 .select(&select_selector)
17 .next()
18 .ok_or("No options found")?;
19
20 let option_selector = Selector::parse("option")?;
21 let transports = select_options
22 .select(&option_selector)
23 .map(|option| {
24 let value = option.value().attr("value").unwrap().to_string();
25 let inner_html = option.inner_html();
26 Ok((value, inner_html))
27 })
28 .collect::<Result<Vec<_>, Box<dyn Error>>>()?;
29
30 Ok(transports)
31}
32
33pub async fn request_transport(transport_type: &str) -> Result<String, Box<dyn Error>> {
34 let url = Url::parse_with_params(
35 "https://bridges.torproject.org/bridges",
36 &[("transport", transport_type)],
37 )?;
38
39 let bridges_page = reqwest::get(url).await?.text().await?;
40 let parsed_page = Html::parse_document(&bridges_page);
41
42
43 let bridgelines_selector = Selector::parse(r#"div[id="bridgelines"]"#)?;
44 let bridge_lines = parsed_page
45 .select(&bridgelines_selector)
46 .next()
47 .map(|div| {
48 div.inner_html()
49 .lines()
50 .map(|line| line.trim().replace("<br>", "\n"))
51 .collect::<String>()
52 })
53 .ok_or("Bridge lines not found")?;
54
55 Ok(bridge_lines)
56}
57
58// The captcha seems to be removed but i will keep the code commented just in case
59/*
60pub async fn request_transport(transport_type: &str) -> Result<(String, String), Box<dyn Error>> {
61 let url = Url::parse_with_params(
62 "https://bridges.torproject.org/bridges",
63 &[("transport", transport_type)],
64 )?;
65
66 let bridges_page = reqwest::get(url).await?.text().await?;
67 let parsed_page = Html::parse_document(&bridges_page);
68
69 let img_selector =
70 Selector::parse(r#"img[alt="Your browser is not displaying images properly."]"#)?;
71 let image_base64 = parsed_page
72 .select(&img_selector)
73 .next()
74 .and_then(|img| img.value().attr("src"))
75 .ok_or("Image source not found")?
76 .to_string();
77
78 let input_selector = Selector::parse(r#"input[name="captcha_challenge_field"]"#)?;
79 let challenge_field = parsed_page
80 .select(&input_selector)
81 .next()
82 .and_then(|input| input.value().attr("value"))
83 .ok_or("Challenge field not found")?
84 .to_string();
85
86 let image_base64 = "".to_string();
87 let challenge_field = "".to_string();
88 Ok((image_base64, challenge_field))
89}
90
91pub async fn submit_answer(
92 transport_type: &str,
93 challenge_field: String,
94 captcha_response: String,
95) -> Result<String, Box<dyn Error>> {
96 let mut challenge_data: HashMap<&str, String> = HashMap::new();
97 challenge_data.insert("captcha_challenge_field", challenge_field);
98 challenge_data.insert("captcha_response_field", captcha_response);
99 challenge_data.insert("submit", "submit".to_string());
100
101 let client = Client::new();
102 let url = Url::parse_with_params(
103 "https://bridges.torproject.org/bridges",
104 &[("transport", transport_type)],
105 )?;
106 let response_page = client
107 .post(url)
108 .form(&challenge_data)
109 .send()
110 .await?
111 .text()
112 .await?;
113
114 let parsed_page = Html::parse_document(&response_page);
115
116 let bridgelines_selector = Selector::parse(r#"div[id="bridgelines"]"#)?;
117 let bridge_lines = parsed_page
118 .select(&bridgelines_selector)
119 .next()
120 .map(|div| {
121 div.inner_html()
122 .lines()
123 .map(|line| line.trim().replace("<br>", "\n"))
124 .collect::<String>()
125 })
126 .ok_or("Bridge lines not found")?;
127
128 Ok(bridge_lines)
129}
130*/