Skip to main content

egs_api/api/
mod.rs

1use log::{error, warn};
2use reqwest::header::HeaderMap;
3use reqwest::{Client, RequestBuilder};
4use serde::de::DeserializeOwned;
5use types::account::UserData;
6use url::Url;
7
8use crate::api::error::EpicAPIError;
9
10/// Module holding the API types
11pub mod types;
12
13/// Various API Utils
14pub mod utils;
15
16/// Binary reader/writer for manifest parsing
17#[allow(dead_code)]
18pub(crate) mod binary_rw;
19
20/// Error type
21pub mod error;
22
23/// Fab Methods
24pub mod fab;
25
26///Account methods
27pub mod account;
28
29/// EGS Methods
30pub mod egs;
31/// Session Handling
32pub mod login;
33
34/// Commerce Methods (pricing, purchases, billing)
35pub mod commerce;
36
37/// Service Status Methods
38pub mod status;
39
40/// Presence Methods
41pub mod presence;
42
43#[derive(Debug, Clone)]
44pub(crate) struct EpicAPI {
45    client: Client,
46    pub(crate) user_data: UserData,
47}
48
49impl Default for EpicAPI {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl EpicAPI {
56    pub fn new() -> Self {
57        let mut headers = HeaderMap::new();
58        headers.insert(
59            "User-Agent",
60            "UELauncher/17.0.1-37584233+++Portal+Release-Live Windows/10.0.19043.1.0.64bit"
61                .parse()
62                .unwrap(),
63        );
64        headers.insert(
65            "X-Epic-Correlation-ID",
66            "UE4-c176f7154c2cda1061cc43ab52598e2b-93AFB486488A22FDF70486BD1D883628-BFCD88F649E997BA203FF69F07CE578C".parse().unwrap()
67        );
68        let client = reqwest::Client::builder()
69            .default_headers(headers)
70            .cookie_store(true)
71            .build()
72            .unwrap();
73        EpicAPI {
74            client,
75            user_data: Default::default(),
76        }
77    }
78
79    fn authorized_get_client(&self, url: Url) -> RequestBuilder {
80        self.set_authorization_header(self.client.get(url))
81    }
82
83    fn authorized_post_client(&self, url: Url) -> RequestBuilder {
84        self.set_authorization_header(self.client.post(url))
85    }
86
87    fn set_authorization_header(&self, rb: RequestBuilder) -> RequestBuilder {
88        rb.header(
89            "Authorization",
90            format!(
91                "{} {}",
92                self.user_data
93                    .token_type
94                    .as_ref()
95                    .unwrap_or(&"bearer".to_string()),
96                self.user_data
97                    .access_token
98                    .as_ref()
99                    .unwrap_or(&"".to_string())
100            ),
101        )
102    }
103
104    /// Send an authorized GET request and deserialize the JSON response
105    pub(crate) async fn authorized_get_json<T: DeserializeOwned>(
106        &self,
107        url: &str,
108    ) -> Result<T, EpicAPIError> {
109        let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
110        let response = self
111            .authorized_get_client(parsed_url)
112            .send()
113            .await
114            .map_err(|e| {
115                error!("{:?}", e);
116                EpicAPIError::NetworkError(e)
117            })?;
118        if response.status() == reqwest::StatusCode::OK {
119            response.json::<T>().await.map_err(|e| {
120                error!("{:?}", e);
121                EpicAPIError::DeserializationError(format!("{}", e))
122            })
123        } else {
124            let status = response.status();
125            let body = response.text().await.unwrap_or_default();
126            warn!("{} result: {}", status, body);
127            Err(EpicAPIError::HttpError { status, body })
128        }
129    }
130
131    /// Send an authorized POST request with form data and deserialize the JSON response
132    pub(crate) async fn authorized_post_form_json<T: DeserializeOwned>(
133        &self,
134        url: &str,
135        form: &[(String, String)],
136    ) -> Result<T, EpicAPIError> {
137        let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
138        let response = self
139            .authorized_post_client(parsed_url)
140            .form(form)
141            .send()
142            .await
143            .map_err(|e| {
144                error!("{:?}", e);
145                EpicAPIError::NetworkError(e)
146            })?;
147        if response.status() == reqwest::StatusCode::OK {
148            response.json::<T>().await.map_err(|e| {
149                error!("{:?}", e);
150                EpicAPIError::DeserializationError(format!("{}", e))
151            })
152        } else {
153            let status = response.status();
154            let body = response.text().await.unwrap_or_default();
155            warn!("{} result: {}", status, body);
156            Err(EpicAPIError::HttpError { status, body })
157        }
158    }
159
160    /// Send an authorized POST request with a JSON body and deserialize the JSON response
161    pub(crate) async fn authorized_post_json<T: DeserializeOwned, B: serde::Serialize>(
162        &self,
163        url: &str,
164        body: &B,
165    ) -> Result<T, EpicAPIError> {
166        let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
167        let response = self
168            .authorized_post_client(parsed_url)
169            .json(body)
170            .send()
171            .await
172            .map_err(|e| {
173                error!("{:?}", e);
174                EpicAPIError::NetworkError(e)
175            })?;
176        if response.status() == reqwest::StatusCode::OK {
177            response.json::<T>().await.map_err(|e| {
178                error!("{:?}", e);
179                EpicAPIError::DeserializationError(format!("{}", e))
180            })
181        } else {
182            let status = response.status();
183            let body = response.text().await.unwrap_or_default();
184            warn!("{} result: {}", status, body);
185            Err(EpicAPIError::HttpError { status, body })
186        }
187    }
188
189    /// Send an unauthenticated GET request and return the raw bytes
190    pub(crate) async fn get_bytes(&self, url: &str) -> Result<Vec<u8>, EpicAPIError> {
191        let parsed_url = Url::parse(url).map_err(|_| EpicAPIError::InvalidParams)?;
192        let response = self
193            .client
194            .get(parsed_url)
195            .send()
196            .await
197            .map_err(|e| {
198                error!("{:?}", e);
199                EpicAPIError::NetworkError(e)
200            })?;
201        if response.status() == reqwest::StatusCode::OK {
202            response.bytes().await.map(|b| b.to_vec()).map_err(|e| {
203                error!("{:?}", e);
204                EpicAPIError::DeserializationError(format!("{}", e))
205            })
206        } else {
207            let status = response.status();
208            let body = response.text().await.unwrap_or_default();
209            warn!("{} result: {}", status, body);
210            Err(EpicAPIError::HttpError { status, body })
211        }
212    }
213}