bard_api_rs/
lib.rs

1use rand::Rng;
2use reqwest::header;
3use serde::{Deserialize, Serialize};
4use serde_json::json;
5
6#[derive(Debug, Serialize, Deserialize)]
7pub struct ChatSession {
8    request_id: i32,
9    snlm0e: String,
10    cfb2h: String,
11    last_conversation_id: String,
12    last_response_id: String,
13    last_choice_id: String,
14}
15
16impl ChatSession {
17    pub async fn new() -> Self {
18        let mut client = reqwest::Client::builder();
19        if let Ok(proxy) = std::env::var("https_proxy") {
20            client = client.proxy(reqwest::Proxy::https(&proxy).unwrap());
21        }
22        let mut headers = header::HeaderMap::new();
23        let secure_1psid = std::env::var("Secure_1PSID").unwrap();
24        let secure_1psid = format!("__Secure-1PSID={secure_1psid}");
25        headers.insert(
26            "Cookie",
27            header::HeaderValue::from_str(&secure_1psid).unwrap(),
28        );
29        let response = client
30            .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
31            .default_headers(headers)
32            .build()
33            .unwrap()
34            .get("https://bard.google.com/").send().await.unwrap();
35        let response_text = response.text().await.unwrap();
36        let snlm0e_search_result = regex::Regex::new(r#"SNlM0e":"(.*?)""#)
37            .unwrap()
38            .captures(&response_text);
39        let cfb2h_search_result = regex::Regex::new(r#"cfb2h":"(.*?)""#)
40            .unwrap()
41            .captures(&response_text);
42        if snlm0e_search_result.is_none() || cfb2h_search_result.is_none() {
43            panic!("Cannot find snlm0e or cfb2h");
44        } else {
45            let snlm0e = snlm0e_search_result
46                .unwrap()
47                .get(1)
48                .unwrap()
49                .as_str()
50                .to_string();
51            let cfb2h = cfb2h_search_result
52                .unwrap()
53                .get(1)
54                .unwrap()
55                .as_str()
56                .to_string();
57            let request_id = rand::thread_rng().gen_range(100000..999999);
58            ChatSession {
59                request_id,
60                snlm0e,
61                cfb2h,
62                last_conversation_id: "".to_string(),
63                last_response_id: "".to_string(),
64                last_choice_id: "".to_string(),
65            }
66        }
67    }
68
69    fn parse_response(&self, response: &str) -> (String, String, String, String) {
70        let mut lines = response.split('\n');
71        let the_line = lines.find(|it| it.contains("wrb.fr")).unwrap();
72        let parsed_line = serde_json::from_str::<Vec<Vec<serde_json::Value>>>(the_line).unwrap();
73        let inner_str = parsed_line[0][2].as_str().unwrap();
74        let inner = serde_json::from_str::<Vec<Vec<serde_json::Value>>>(&inner_str).unwrap();
75        let text_response = inner[0][0].as_str().unwrap();
76        let c_and_r = [inner[1][0].as_str().unwrap(), inner[1][1].as_str().unwrap()];
77        let rc = inner[4][0].as_array().unwrap()[0].as_str().unwrap();
78        return (
79            text_response.to_string(),
80            c_and_r[0].to_string(),
81            c_and_r[1].to_string(),
82            rc.to_string(),
83        );
84    }
85
86    pub async fn send_message(&mut self, text: &str) -> String {
87        let input_text_struct = json!([
88            [text.to_string()],
89            null,
90            [
91                self.last_conversation_id.clone(),
92                self.last_response_id.clone(),
93                self.last_choice_id.clone(),
94            ],
95        ]);
96        let input_text = serde_json::to_string(&input_text_struct).unwrap();
97        let mut params = Vec::new();
98        params.push(("bl", self.cfb2h.clone()));
99        params.push(("_reqid", self.request_id.to_string()));
100        params.push(("rt", "c".to_string()));
101        params.push((
102            "f.req",
103            serde_json::to_string(&json!([null, input_text])).unwrap(),
104        ));
105        params.push(("at", self.snlm0e.clone()));
106        let mut client = reqwest::Client::builder();
107        if let Ok(proxy) = std::env::var("https_proxy") {
108            client = client.proxy(reqwest::Proxy::https(&proxy).unwrap());
109        }
110        let mut headers = header::HeaderMap::new();
111        let secure_1psid = std::env::var("Secure_1PSID").unwrap();
112        let secure_1psid = format!("__Secure-1PSID={secure_1psid}");
113        headers.insert(
114            "Cookie",
115            header::HeaderValue::from_str(&secure_1psid).unwrap(),
116        );
117        let resp = client
118            .default_headers(headers)
119            .build()
120            .unwrap()
121            .post("https://bard.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate")
122            .header("Content-Type", "application/x-www-form-urlencoded")
123            .form(&params)
124            .send()
125            .await
126            .unwrap();
127        let resp_text = resp.text().await.unwrap();
128        let (text_response, conversation_id, response_id, choice_id) =
129            self.parse_response(&resp_text);
130        self.last_conversation_id = conversation_id;
131        self.last_response_id = response_id;
132        self.last_choice_id = choice_id;
133        self.request_id += 100000;
134        text_response
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[tokio::test]
143    async fn it_works() {
144        let mut session = ChatSession::new().await;
145        let response = session.send_message("Hello").await;
146        println!("{}", response);
147    }
148}