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*/