1use crate::{errors::ApiError, errors::RobloxApiErrorResponse, ApiResult, Client};
2use reqwest::{header, Method, RequestBuilder, Response};
3use serde::de::{self, DeserializeOwned};
4
5#[derive(Debug, Clone)]
6pub struct Https {
7 pub client: reqwest::Client,
8}
9
10impl Default for Https {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl Client {
17 pub async fn set_cookie(&mut self, cookie: &str) -> &mut Self {
33 let mut headers = header::HeaderMap::new();
34
35 headers.insert(
36 header::COOKIE,
37 header::HeaderValue::from_str(&(".ROBLOSECURITY=".to_owned() + cookie)).unwrap(),
38 );
39 headers.insert(
40 header::CONTENT_LENGTH,
41 header::HeaderValue::from_static("0"),
42 );
43
44 headers.insert(
46 header::HeaderName::from_static("x-csrf-token"),
47 header::HeaderValue::from(
48 reqwest::Client::new()
49 .post("https://auth.roblox.com/v2/logout")
50 .header("content-length", "0")
51 .send()
52 .await
53 .expect("Failed to get X-CSRF-TOKEN")
54 .headers()
55 .get("x-csrf-token")
56 .unwrap_or(&header::HeaderValue::from_static("")),
57 ),
58 );
59
60 self.session.client = reqwest::Client::builder()
62 .cookie_store(true)
63 .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.152 Safari/537.36")
64 .default_headers(headers)
65 .build()
66 .expect("Failed to build new client from headers");
67
68 self.session.validate_cookie().await;
70
71 self
72 }
73}
74
75impl Https {
76 pub fn new() -> Self {
78 Self {
79 client: reqwest::Client::builder()
80 .cookie_store(true)
81 .build()
82 .unwrap(),
83 }
84 }
85
86 async fn de_to_result<T>(req: Response) -> ApiResult<T>
87 where
88 T: DeserializeOwned,
89 {
90 let status_code = req.status();
91 let data = req.bytes().await?;
92
93 if let Ok(error) = serde_json::from_slice::<RobloxApiErrorResponse>(&data) {
94 if !error.is_empty() {
95 return Err(ApiError::Roblox {
96 status_code,
97 reason: error.reason().unwrap_or_else(|| "Unknown error".to_owned()),
98 });
99 }
100 }
101 Ok(serde_json::from_slice::<T>(&data)?)
102 }
103
104 pub async fn request<T>(&mut self, method: Method, request_url: &str) -> ApiResult<T>
106 where
107 T: de::DeserializeOwned,
108 {
109 let response = self
111 .client
112 .request(method.clone(), request_url)
113 .send()
114 .await
115 .expect("Request failed");
116
117 return Https::de_to_result::<T>(response).await;
118 }
119
120 pub async fn post(&mut self, request_url: &str) -> RequestBuilder {
121 self.client.post(request_url)
122 }
123
124 async fn validate_cookie(&mut self) {
126 let req = self
127 .client
128 .request(Method::GET, "https://www.roblox.com/mobileapi/userinfo")
129 .send()
130 .await
131 .expect("Failed to get user info");
132
133 let _: serde_json::Value = req.json().await.expect("Failed to validate cookie");
134 }
135}