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