Skip to main content

discord_selfbot/http/
mod.rs

1use crate::{Error, Result};
2use crate::utils::Snowflake;
3use crate::models::{Message, Channel, User};
4use reqwest::{Client as ReqwestClient, header};
5use serde_json::json;
6use std::sync::Arc;
7use tracing::{debug, info};
8
9pub struct HttpClient {
10    client: ReqwestClient,
11    token: String,
12    base_url: String,
13}
14
15impl HttpClient {
16    pub fn new(token: String) -> Self {
17        let mut headers = header::HeaderMap::new();
18        headers.insert(
19            header::AUTHORIZATION,
20            header::HeaderValue::from_str(&token).unwrap(),
21        );
22        headers.insert(
23            header::CONTENT_TYPE,
24            header::HeaderValue::from_static("application/json"),
25        );
26        headers.insert(
27            header::USER_AGENT,
28            header::HeaderValue::from_static("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"),
29        );
30
31        let client = ReqwestClient::builder()
32            .default_headers(headers)
33            .cookie_store(true)
34            .build()
35            .unwrap();
36
37        Self {
38            client,
39            token,
40            base_url: "https://discord.com/api/v9".to_string(),
41        }
42    }
43
44    pub async fn get_current_user(&self) -> Result<User> {
45        let url = format!("{}/users/@me", self.base_url);
46        let response = self.client.get(&url).send().await?;
47
48        if !response.status().is_success() {
49            let status = response.status();
50            let text = response.text().await?;
51            return Err(Error::DiscordApi {
52                code: status.as_u16() as u64,
53                message: text,
54            });
55        }
56
57        let user = response.json().await?;
58        Ok(user)
59    }
60
61    pub async fn get_channel(&self, channel_id: Snowflake) -> Result<Channel> {
62        let url = format!("{}/channels/{}", self.base_url, channel_id);
63        let response = self.client.get(&url).send().await?;
64
65        if !response.status().is_success() {
66            let status = response.status();
67            let text = response.text().await?;
68            return Err(Error::DiscordApi {
69                code: status.as_u16() as u64,
70                message: text,
71            });
72        }
73
74        let channel = response.json().await?;
75        Ok(channel)
76    }
77
78    pub async fn send_message(
79        &self,
80        channel_id: Snowflake,
81        payload: serde_json::Value,
82    ) -> Result<Message> {
83        let url = format!("{}/channels/{}/messages", self.base_url, channel_id);
84        
85        debug!("Sending message to channel {}: {:?}", channel_id, payload);
86        
87        let response = self.client
88            .post(&url)
89            .json(&payload)
90            .send()
91            .await?;
92
93        if !response.status().is_success() {
94            let status = response.status();
95            let text = response.text().await?;
96            return Err(Error::DiscordApi {
97                code: status.as_u16() as u64,
98                message: text,
99            });
100        }
101
102        let message = response.json().await?;
103        Ok(message)
104    }
105
106    pub async fn edit_message(
107        &self,
108        channel_id: Snowflake,
109        message_id: Snowflake,
110        payload: serde_json::Value,
111    ) -> Result<Message> {
112        let url = format!("{}/channels/{}/messages/{}", self.base_url, channel_id, message_id);
113        
114        let response = self.client
115            .patch(&url)
116            .json(&payload)
117            .send()
118            .await?;
119
120        if !response.status().is_success() {
121            let status = response.status();
122            let text = response.text().await?;
123            return Err(Error::DiscordApi {
124                code: status.as_u16() as u64,
125                message: text,
126            });
127        }
128
129        let message = response.json().await?;
130        Ok(message)
131    }
132
133    pub async fn delete_message(
134        &self,
135        channel_id: Snowflake,
136        message_id: Snowflake,
137    ) -> Result<()> {
138        let url = format!("{}/channels/{}/messages/{}", self.base_url, channel_id, message_id);
139        
140        let response = self.client
141            .delete(&url)
142            .send()
143            .await?;
144
145        if !response.status().is_success() {
146            let status = response.status();
147            let text = response.text().await?;
148            return Err(Error::DiscordApi {
149                code: status.as_u16() as u64,
150                message: text,
151            });
152        }
153
154        Ok(())
155    }
156
157    pub async fn get_message(
158        &self,
159        channel_id: Snowflake,
160        message_id: Snowflake,
161    ) -> Result<Message> {
162        let url = format!("{}/channels/{}/messages/{}", self.base_url, channel_id, message_id);
163        
164        let response = self.client.get(&url).send().await?;
165
166        if !response.status().is_success() {
167            let status = response.status();
168            let text = response.text().await?;
169            return Err(Error::DiscordApi {
170                code: status.as_u16() as u64,
171                message: text,
172            });
173        }
174
175        let message = response.json().await?;
176        Ok(message)
177    }
178
179    pub async fn add_reaction(
180        &self,
181        channel_id: Snowflake,
182        message_id: Snowflake,
183        emoji: &str,
184    ) -> Result<()> {
185        let url = format!(
186            "{}/channels/{}/messages/{}/reactions/{}/@me",
187            self.base_url, channel_id, message_id, emoji
188        );
189        
190        let response = self.client
191            .put(&url)
192            .send()
193            .await?;
194
195        if !response.status().is_success() {
196            let status = response.status();
197            let text = response.text().await?;
198            return Err(Error::DiscordApi {
199                code: status.as_u16() as u64,
200                message: text,
201            });
202        }
203
204        Ok(())
205    }
206
207    pub async fn remove_reaction(
208        &self,
209        channel_id: Snowflake,
210        message_id: Snowflake,
211        emoji: &str,
212    ) -> Result<()> {
213        let url = format!(
214            "{}/channels/{}/messages/{}/reactions/{}/@me",
215            self.base_url, channel_id, message_id, emoji
216        );
217        
218        let response = self.client
219            .delete(&url)
220            .send()
221            .await?;
222
223        if !response.status().is_success() {
224            let status = response.status();
225            let text = response.text().await?;
226            return Err(Error::DiscordApi {
227                code: status.as_u16() as u64,
228                message: text,
229            });
230        }
231
232        Ok(())
233    }
234
235    pub async fn vote_poll(
236        &self,
237        channel_id: Snowflake,
238        message_id: Snowflake,
239        answer_id: u32,
240    ) -> Result<()> {
241        let url = format!(
242            "{}/channels/{}/polls/{}/answers/{}",
243            self.base_url, channel_id, message_id, answer_id
244        );
245        
246        let response = self.client
247            .put(&url)
248            .send()
249            .await?;
250
251        if !response.status().is_success() {
252            let status = response.status();
253            let text = response.text().await?;
254            return Err(Error::DiscordApi {
255                code: status.as_u16() as u64,
256                message: text,
257            });
258        }
259
260        Ok(())
261    }
262
263    pub async fn typing(&self, channel_id: Snowflake) -> Result<()> {
264        let url = format!("{}/channels/{}/typing", self.base_url, channel_id);
265        
266        let response = self.client
267            .post(&url)
268            .send()
269            .await?;
270
271        if !response.status().is_success() {
272            let status = response.status();
273            let text = response.text().await?;
274            return Err(Error::DiscordApi {
275                code: status.as_u16() as u64,
276                message: text,
277            });
278        }
279
280        Ok(())
281    }
282}
283