1use reqwest::{
7 header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE, USER_AGENT},
8 StatusCode,
9};
10use crate::error::ClientError;
11use crate::model::*;
12use serde::de::DeserializeOwned;
13use serde_json::json;
14
15pub struct Http {
31 pub client: reqwest::Client,
32 pub base_url: String,
33 token: String,
34}
35
36impl Http {
37 pub fn new(token: &str, base_url: String) -> Self {
40 Self::new_with_prefix(token, base_url, &format!("Bot {}", token))
41 }
42
43 pub fn new_user(token: &str, base_url: String) -> Self {
45 Self::new_with_prefix(token, base_url, token)
46 }
47
48 fn new_with_prefix(token: &str, base_url: String, auth_value: &str) -> Self {
49 let mut headers = HeaderMap::new();
50 headers.insert(AUTHORIZATION, HeaderValue::from_str(auth_value).unwrap());
51 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
52 headers.insert(USER_AGENT, HeaderValue::from_static("fluxer-rust"));
53
54 Self {
55 client: reqwest::Client::builder()
56 .default_headers(headers)
57 .build()
58 .unwrap(),
59 base_url,
60 token: token.to_string(),
61 }
62 }
63
64 pub fn get_token(&self) -> &str {
65 &self.token
66 }
67
68 async fn request_json<T: DeserializeOwned>(
69 &self,
70 req: reqwest::RequestBuilder,
71 ) -> Result<T, ClientError> {
72 let resp = req.send().await.map_err(ClientError::Http)?;
73 let status = resp.status();
74 if status == StatusCode::NO_CONTENT {
75 return Err(ClientError::Api("Expected body but got 204".into()));
76 }
77 if !status.is_success() {
78 let text = resp.text().await.unwrap_or_default();
79 return Err(ClientError::Api(format!("HTTP {}: {}", status, text)));
80 }
81 resp.json::<T>().await.map_err(ClientError::Http)
82 }
83
84 async fn request_empty(&self, req: reqwest::RequestBuilder) -> Result<(), ClientError> {
85 let resp = req.send().await.map_err(ClientError::Http)?;
86 let status = resp.status();
87 if !status.is_success() {
88 let text = resp.text().await.unwrap_or_default();
89 return Err(ClientError::Api(format!("HTTP {}: {}", status, text)));
90 }
91 Ok(())
92 }
93
94 pub async fn get_gateway(&self) -> Result<String, ClientError> {
96 let url = format!("{}/gateway/bot", self.base_url);
97 let res = self
98 .request_json::<GatewayBotResponse>(self.client.get(&url))
99 .await?;
100 Ok(res.url)
101 }
102
103 pub async fn get_well_known_fluxer(&self) -> Result<WellKnownFluxerResponse, ClientError> {
105 let mut url = reqwest::Url::parse(&self.base_url)
106 .map_err(|e| ClientError::Api(format!("invalid base URL: {}", e)))?;
107 let mut path = url.path().trim_end_matches('/').to_string();
108 if let Some(prefix) = path.strip_suffix("/v1") {
109 path = prefix.to_string();
110 }
111 if path.is_empty() {
112 path.push('/');
113 }
114 if !path.ends_with('/') {
115 path.push('/');
116 }
117 path.push_str(".well-known/fluxer");
118 url.set_path(&path);
119 url.set_query(None);
120 self.request_json(self.client.get(url)).await
121 }
122
123 pub async fn get_me(&self) -> Result<User, ClientError> {
125 let url = format!("{}/users/@me", self.base_url);
126 self.request_json(self.client.get(&url)).await
127 }
128
129 pub async fn get_user(&self, user_id: &str) -> Result<User, ClientError> {
131 let url = format!("{}/users/{}", self.base_url, user_id);
132 self.request_json(self.client.get(&url)).await
133 }
134
135 pub async fn get_current_user_guilds(&self) -> Result<Vec<Guild>, ClientError> {
137 let url = format!("{}/users/@me/guilds", self.base_url);
138 self.request_json(self.client.get(&url)).await
139 }
140
141 pub async fn get_channel(&self, channel_id: &str) -> Result<Channel, ClientError> {
143 let url = format!("{}/channels/{}", self.base_url, channel_id);
144 self.request_json(self.client.get(&url)).await
145 }
146
147 pub async fn edit_channel(
149 &self,
150 channel_id: &str,
151 payload: &ChannelCreatePayload,
152 ) -> Result<Channel, ClientError> {
153 let url = format!("{}/channels/{}", self.base_url, channel_id);
154 self.request_json(self.client.patch(&url).json(payload)).await
155 }
156
157 pub async fn delete_channel(&self, channel_id: &str) -> Result<(), ClientError> {
159 let url = format!("{}/channels/{}", self.base_url, channel_id);
160 self.request_empty(self.client.delete(&url)).await
161 }
162
163 pub async fn trigger_typing(&self, channel_id: &str) -> Result<(), ClientError> {
166 let url = format!("{}/channels/{}/typing", self.base_url, channel_id);
167 self.request_empty(self.client.post(&url).body("{}")).await
168 }
169
170 pub async fn get_messages(
184 &self,
185 channel_id: &str,
186 query: GetMessagesQuery,
187 ) -> Result<Vec<Message>, ClientError> {
188 let url = format!(
189 "{}/channels/{}/messages{}",
190 self.base_url,
191 channel_id,
192 query.to_query_string()
193 );
194 self.request_json(self.client.get(&url)).await
195 }
196
197 pub async fn get_message(
199 &self,
200 channel_id: &str,
201 message_id: &str,
202 ) -> Result<Message, ClientError> {
203 let url = format!(
204 "{}/channels/{}/messages/{}",
205 self.base_url, channel_id, message_id
206 );
207 self.request_json(self.client.get(&url)).await
208 }
209
210 pub async fn send_message(
213 &self,
214 channel_id: &str,
215 content: &str,
216 ) -> Result<Message, ClientError> {
217 let url = format!("{}/channels/{}/messages", self.base_url, channel_id);
218 let body = json!({ "content": content });
219 self.request_json(self.client.post(&url).json(&body)).await
220 }
221
222 pub async fn send_message_advanced(
224 &self,
225 channel_id: &str,
226 payload: &MessageCreatePayload,
227 ) -> Result<Message, ClientError> {
228 let url = format!("{}/channels/{}/messages", self.base_url, channel_id);
229 self.request_json(self.client.post(&url).json(payload)).await
230 }
231
232 pub async fn send_embed(
234 &self,
235 channel_id: &str,
236 content: Option<&str>,
237 embeds: Vec<Embed>,
238 ) -> Result<Message, ClientError> {
239 let payload = MessageCreatePayload {
240 content: content.map(|s| s.to_string()),
241 embeds: Some(embeds),
242 ..Default::default()
243 };
244 self.send_message_advanced(channel_id, &payload).await
245 }
246
247 pub async fn reply_to_message(
250 &self,
251 channel_id: &str,
252 message_id: &str,
253 content: &str,
254 ) -> Result<Message, ClientError> {
255 let payload = MessageCreatePayload {
256 content: Some(content.to_string()),
257 message_reference: Some(MessageReference {
258 message_id: message_id.to_string(),
259 channel_id: None,
260 guild_id: None,
261 fail_if_not_exists: Some(true),
262 }),
263 ..Default::default()
264 };
265 self.send_message_advanced(channel_id, &payload).await
266 }
267
268 pub async fn edit_message(
270 &self,
271 channel_id: &str,
272 message_id: &str,
273 content: &str,
274 ) -> Result<Message, ClientError> {
275 let url = format!(
276 "{}/channels/{}/messages/{}",
277 self.base_url, channel_id, message_id
278 );
279 let body = json!({ "content": content });
280 self.request_json(self.client.patch(&url).json(&body)).await
281 }
282
283 pub async fn edit_message_advanced(
285 &self,
286 channel_id: &str,
287 message_id: &str,
288 payload: &MessageCreatePayload,
289 ) -> Result<Message, ClientError> {
290 let url = format!(
291 "{}/channels/{}/messages/{}",
292 self.base_url, channel_id, message_id
293 );
294 self.request_json(self.client.patch(&url).json(payload)).await
295 }
296
297 pub async fn delete_message(
299 &self,
300 channel_id: &str,
301 message_id: &str,
302 ) -> Result<(), ClientError> {
303 let url = format!(
304 "{}/channels/{}/messages/{}",
305 self.base_url, channel_id, message_id
306 );
307 self.request_empty(self.client.delete(&url)).await
308 }
309
310 pub async fn bulk_delete_messages(
312 &self,
313 channel_id: &str,
314 message_ids: Vec<&str>,
315 ) -> Result<(), ClientError> {
316 let url = format!(
317 "{}/channels/{}/messages/bulk-delete",
318 self.base_url, channel_id
319 );
320 let body = json!({ "message_ids": message_ids });
321 self.request_empty(self.client.post(&url).json(&body)).await
322 }
323
324 pub async fn add_reaction(
328 &self,
329 channel_id: &str,
330 message_id: &str,
331 emoji: &str,
332 ) -> Result<(), ClientError> {
333 let encoded = urlencoded(emoji);
334 let url = format!(
335 "{}/channels/{}/messages/{}/reactions/{}/@me",
336 self.base_url, channel_id, message_id, encoded
337 );
338 self.request_empty(self.client.put(&url).body("")).await
339 }
340
341 pub async fn remove_own_reaction(
343 &self,
344 channel_id: &str,
345 message_id: &str,
346 emoji: &str,
347 ) -> Result<(), ClientError> {
348 let encoded = urlencoded(emoji);
349 let url = format!(
350 "{}/channels/{}/messages/{}/reactions/{}/@me",
351 self.base_url, channel_id, message_id, encoded
352 );
353 self.request_empty(self.client.delete(&url)).await
354 }
355
356 pub async fn remove_user_reaction(
358 &self,
359 channel_id: &str,
360 message_id: &str,
361 emoji: &str,
362 user_id: &str,
363 ) -> Result<(), ClientError> {
364 let encoded = urlencoded(emoji);
365 let url = format!(
366 "{}/channels/{}/messages/{}/reactions/{}/{}",
367 self.base_url, channel_id, message_id, encoded, user_id
368 );
369 self.request_empty(self.client.delete(&url)).await
370 }
371
372 pub async fn get_reactions(
374 &self,
375 channel_id: &str,
376 message_id: &str,
377 emoji: &str,
378 ) -> Result<Vec<User>, ClientError> {
379 let encoded = urlencoded(emoji);
380 let url = format!(
381 "{}/channels/{}/messages/{}/reactions/{}",
382 self.base_url, channel_id, message_id, encoded
383 );
384 self.request_json(self.client.get(&url)).await
385 }
386
387 pub async fn clear_reactions(
389 &self,
390 channel_id: &str,
391 message_id: &str,
392 ) -> Result<(), ClientError> {
393 let url = format!(
394 "{}/channels/{}/messages/{}/reactions",
395 self.base_url, channel_id, message_id
396 );
397 self.request_empty(self.client.delete(&url)).await
398 }
399
400 pub async fn clear_reactions_for_emoji(
402 &self,
403 channel_id: &str,
404 message_id: &str,
405 emoji: &str,
406 ) -> Result<(), ClientError> {
407 let encoded = urlencoded(emoji);
408 let url = format!(
409 "{}/channels/{}/messages/{}/reactions/{}",
410 self.base_url, channel_id, message_id, encoded
411 );
412 self.request_empty(self.client.delete(&url)).await
413 }
414
415 pub async fn get_pins(&self, channel_id: &str) -> Result<PinsResponse, ClientError> {
417 let url = format!("{}/channels/{}/messages/pins", self.base_url, channel_id);
418 self.request_json(self.client.get(&url)).await
419 }
420
421 pub async fn pin_message(&self, channel_id: &str, message_id: &str) -> Result<(), ClientError> {
423 let url = format!(
424 "{}/channels/{}/pins/{}",
425 self.base_url, channel_id, message_id
426 );
427 self.request_empty(self.client.put(&url).body("")).await
428 }
429
430 pub async fn unpin_message(
432 &self,
433 channel_id: &str,
434 message_id: &str,
435 ) -> Result<(), ClientError> {
436 let url = format!(
437 "{}/channels/{}/pins/{}",
438 self.base_url, channel_id, message_id
439 );
440 self.request_empty(self.client.delete(&url)).await
441 }
442
443 pub async fn get_invite(&self, invite_code: &str) -> Result<Invite, ClientError> {
445 let url = format!("{}/invites/{}?with_counts=true", self.base_url, invite_code);
446 self.request_json(self.client.get(&url)).await
447 }
448
449 pub async fn create_invite(
451 &self,
452 channel_id: &str,
453 payload: &CreateInvitePayload,
454 ) -> Result<Invite, ClientError> {
455 let url = format!("{}/channels/{}/invites", self.base_url, channel_id);
456 self.request_json(self.client.post(&url).json(payload)).await
457 }
458
459 pub async fn delete_invite(&self, invite_code: &str) -> Result<(), ClientError> {
461 let url = format!("{}/invites/{}", self.base_url, invite_code);
462 self.request_empty(self.client.delete(&url)).await
463 }
464
465 pub async fn accept_invite(&self, invite_code: &str) -> Result<Invite, ClientError> {
467 let url = format!("{}/invites/{}", self.base_url, invite_code);
468 self.request_json(self.client.post(&url).body("")).await
469 }
470
471 pub async fn get_channel_invites(&self, channel_id: &str) -> Result<Vec<Invite>, ClientError> {
473 let url = format!("{}/channels/{}/invites", self.base_url, channel_id);
474 self.request_json(self.client.get(&url)).await
475 }
476
477 pub async fn get_guild_invites(&self, guild_id: &str) -> Result<Vec<Invite>, ClientError> {
479 let url = format!("{}/guilds/{}/invites", self.base_url, guild_id);
480 self.request_json(self.client.get(&url)).await
481 }
482
483 pub async fn get_guild(&self, guild_id: &str) -> Result<Guild, ClientError> {
485 let url = format!("{}/guilds/{}", self.base_url, guild_id);
486 self.request_json(self.client.get(&url)).await
487 }
488
489 pub async fn edit_guild(
491 &self,
492 guild_id: &str,
493 payload: &EditGuildPayload,
494 ) -> Result<Guild, ClientError> {
495 let url = format!("{}/guilds/{}", self.base_url, guild_id);
496 self.request_json(self.client.patch(&url).json(payload)).await
497 }
498
499 pub async fn delete_guild(&self, guild_id: &str) -> Result<(), ClientError> {
501 let url = format!("{}/guilds/{}/delete", self.base_url, guild_id);
502 self.request_empty(self.client.post(&url).body("")).await
503 }
504
505 pub async fn create_guild(&self, payload: &GuildCreatePayload) -> Result<Guild, ClientError> {
507 let url = format!("{}/guilds", self.base_url);
508 self.request_json(self.client.post(&url).json(payload))
509 .await
510 }
511
512 pub async fn get_guild_channels(&self, guild_id: &str) -> Result<Vec<Channel>, ClientError> {
514 let url = format!("{}/guilds/{}/channels", self.base_url, guild_id);
515 self.request_json(self.client.get(&url)).await
516 }
517
518 pub async fn create_channel(
520 &self,
521 guild_id: &str,
522 payload: &ChannelCreatePayload,
523 ) -> Result<Channel, ClientError> {
524 let url = format!("{}/guilds/{}/channels", self.base_url, guild_id);
525 self.request_json(self.client.post(&url).json(payload)).await
526 }
527
528 pub async fn get_guild_member(
530 &self,
531 guild_id: &str,
532 user_id: &str,
533 ) -> Result<Member, ClientError> {
534 let url = format!("{}/guilds/{}/members/{}", self.base_url, guild_id, user_id);
535 self.request_json(self.client.get(&url)).await
536 }
537
538 pub async fn get_guild_members(
540 &self,
541 guild_id: &str,
542 limit: Option<u16>,
543 after: Option<&str>,
544 ) -> Result<Vec<Member>, ClientError> {
545 let mut url = format!("{}/guilds/{}/members?", self.base_url, guild_id);
546 if let Some(l) = limit {
547 url.push_str(&format!("limit={}&", l.min(1000)));
548 }
549 if let Some(a) = after {
550 url.push_str(&format!("after={}", a));
551 }
552 self.request_json(self.client.get(&url)).await
553 }
554
555 pub async fn kick_member(&self, guild_id: &str, user_id: &str) -> Result<(), ClientError> {
557 let url = format!("{}/guilds/{}/members/{}", self.base_url, guild_id, user_id);
558 self.request_empty(self.client.delete(&url)).await
559 }
560
561 pub async fn edit_member(
564 &self,
565 guild_id: &str,
566 user_id: &str,
567 payload: &EditMemberPayload,
568 ) -> Result<Member, ClientError> {
569 let url = format!("{}/guilds/{}/members/{}", self.base_url, guild_id, user_id);
570 self.request_json(self.client.patch(&url).json(payload)).await
571 }
572
573 pub async fn add_member_role(
575 &self,
576 guild_id: &str,
577 user_id: &str,
578 role_id: &str,
579 ) -> Result<(), ClientError> {
580 let url = format!(
581 "{}/guilds/{}/members/{}/roles/{}",
582 self.base_url, guild_id, user_id, role_id
583 );
584 self.request_empty(self.client.put(&url).body("")).await
585 }
586
587 pub async fn remove_member_role(
589 &self,
590 guild_id: &str,
591 user_id: &str,
592 role_id: &str,
593 ) -> Result<(), ClientError> {
594 let url = format!(
595 "{}/guilds/{}/members/{}/roles/{}",
596 self.base_url, guild_id, user_id, role_id
597 );
598 self.request_empty(self.client.delete(&url)).await
599 }
600
601 pub async fn ban_member(
603 &self,
604 guild_id: &str,
605 user_id: &str,
606 reason: &str,
607 ) -> Result<(), ClientError> {
608 let url = format!("{}/guilds/{}/bans/{}", self.base_url, guild_id, user_id);
609 let body = json!({ "reason": reason });
610 self.request_empty(self.client.put(&url).json(&body)).await
611 }
612
613 pub async fn unban_member(&self, guild_id: &str, user_id: &str) -> Result<(), ClientError> {
615 let url = format!("{}/guilds/{}/bans/{}", self.base_url, guild_id, user_id);
616 self.request_empty(self.client.delete(&url)).await
617 }
618
619 pub async fn get_guild_bans(
621 &self,
622 guild_id: &str,
623 ) -> Result<Vec<serde_json::Value>, ClientError> {
624 let url = format!("{}/guilds/{}/bans", self.base_url, guild_id);
625 self.request_json(self.client.get(&url)).await
626 }
627
628 pub async fn get_guild_roles(&self, guild_id: &str) -> Result<Vec<Role>, ClientError> {
630 let url = format!("{}/guilds/{}/roles", self.base_url, guild_id);
631 self.request_json(self.client.get(&url)).await
632 }
633
634 pub async fn create_role(
636 &self,
637 guild_id: &str,
638 payload: &CreateRolePayload,
639 ) -> Result<Role, ClientError> {
640 let url = format!("{}/guilds/{}/roles", self.base_url, guild_id);
641 self.request_json(self.client.post(&url).json(payload)).await
642 }
643
644 pub async fn edit_role(
646 &self,
647 guild_id: &str,
648 role_id: &str,
649 payload: &EditRolePayload,
650 ) -> Result<Role, ClientError> {
651 let url = format!("{}/guilds/{}/roles/{}", self.base_url, guild_id, role_id);
652 self.request_json(self.client.patch(&url).json(payload)).await
653 }
654
655 pub async fn delete_role(&self, guild_id: &str, role_id: &str) -> Result<(), ClientError> {
657 let url = format!("{}/guilds/{}/roles/{}", self.base_url, guild_id, role_id);
658 self.request_empty(self.client.delete(&url)).await
659 }
660
661 pub async fn get_guild_emojis(&self, guild_id: &str) -> Result<Vec<Emoji>, ClientError> {
663 let url = format!("{}/guilds/{}/emojis", self.base_url, guild_id);
664 self.request_json(self.client.get(&url)).await
665 }
666
667 pub async fn get_emoji_metadata(
669 &self,
670 emoji_id: &str,
671 ) -> Result<GuildEmojiMetadata, ClientError> {
672 let url = format!("{}/emojis/{}/metadata", self.base_url, emoji_id);
673 self.request_json(self.client.get(&url)).await
674 }
675
676 pub async fn delete_guild_emoji(
678 &self,
679 guild_id: &str,
680 emoji_id: &str,
681 ) -> Result<(), ClientError> {
682 let url = format!("{}/guilds/{}/emojis/{}", self.base_url, guild_id, emoji_id);
683 self.request_empty(self.client.delete(&url)).await
684 }
685
686 pub async fn get_channel_webhooks(
688 &self,
689 channel_id: &str,
690 ) -> Result<Vec<Webhook>, ClientError> {
691 let url = format!("{}/channels/{}/webhooks", self.base_url, channel_id);
692 self.request_json(self.client.get(&url)).await
693 }
694
695 pub async fn get_guild_webhooks(&self, guild_id: &str) -> Result<Vec<Webhook>, ClientError> {
697 let url = format!("{}/guilds/{}/webhooks", self.base_url, guild_id);
698 self.request_json(self.client.get(&url)).await
699 }
700
701 pub async fn create_webhook(
703 &self,
704 channel_id: &str,
705 name: &str,
706 avatar: Option<&str>,
707 ) -> Result<Webhook, ClientError> {
708 let url = format!("{}/channels/{}/webhooks", self.base_url, channel_id);
709 let mut body = json!({ "name": name });
710 if let Some(av) = avatar {
711 body["avatar"] = serde_json::Value::String(av.to_string());
712 }
713 self.request_json(self.client.post(&url).json(&body)).await
714 }
715
716 pub async fn delete_webhook(&self, webhook_id: &str) -> Result<(), ClientError> {
718 let url = format!("{}/webhooks/{}", self.base_url, webhook_id);
719 self.request_empty(self.client.delete(&url)).await
720 }
721
722 pub async fn send_files(
725 &self,
726 channel_id: &str,
727 files: Vec<AttachmentFile>,
728 content: Option<&str>,
729 ) -> Result<Message, ClientError> {
730 let payload = MessageCreatePayload {
731 content: content.map(|s| s.to_string()),
732 ..Default::default()
733 };
734 self.send_message_with_files(channel_id, &payload, files).await
735 }
736
737 pub async fn send_message_with_files(
739 &self,
740 channel_id: &str,
741 payload: &MessageCreatePayload,
742 files: Vec<AttachmentFile>,
743 ) -> Result<Message, ClientError> {
744 use reqwest::multipart::{Form, Part};
745
746 let url = format!("{}/channels/{}/messages", self.base_url, channel_id);
747
748 let mut payload = payload.clone();
749 payload.attachments = Some(
750 files
751 .iter()
752 .enumerate()
753 .map(|(i, f)| AttachmentMetadata {
754 id: i as u64,
755 filename: f.filename.clone(),
756 description: None,
757 })
758 .collect(),
759 );
760 let json_string = serde_json::to_string(&payload)?;
761
762 let mut form = Form::new().text("payload_json", json_string);
763 for (i, file) in files.into_iter().enumerate() {
764 let content_type = file
765 .content_type
766 .unwrap_or_else(|| "application/octet-stream".to_string());
767 let part = Part::bytes(file.data)
768 .file_name(file.filename)
769 .mime_str(&content_type)
770 .map_err(ClientError::Http)?;
771 form = form.part(format!("files[{}]", i), part);
772 }
773
774 let req = self.client.post(&url).multipart(form);
775 self.request_json(req).await
776 }
777
778 pub async fn get_rtc_regions(&self, channel_id: &str) -> Result<Vec<RtcRegion>, ClientError> {
780 let url = format!("{}/channels/{}/rtc-regions", self.base_url, channel_id);
781 self.request_json(self.client.get(&url)).await
782 }
783
784 pub async fn get_channel_slowmode(
786 &self,
787 channel_id: &str,
788 ) -> Result<ChannelSlowmodeState, ClientError> {
789 let url = format!("{}/channels/{}/slowmode", self.base_url, channel_id);
790 self.request_json(self.client.get(&url)).await
791 }
792
793 pub async fn search_messages(
796 &self,
797 query: &SearchMessagesQuery,
798 ) -> Result<SearchMessagesResponse, ClientError> {
799 let url = format!("{}/search/messages", self.base_url);
800 self.request_json(self.client.post(&url).json(query)).await
801 }
802
803 pub async fn ack_bulk(&self, states: Vec<ReadStateAck>) -> Result<(), ClientError> {
806 let url = format!("{}/read-states/ack-bulk", self.base_url);
807 let body = json!({ "read_states": states });
808 self.request_empty(self.client.post(&url).json(&body)).await
809 }
810
811 pub async fn get_guild_audit_logs(
813 &self,
814 guild_id: &str,
815 ) -> Result<GuildAuditLogList, ClientError> {
816 let url = format!("{}/guilds/{}/audit-logs", self.base_url, guild_id);
817 self.request_json(self.client.get(&url)).await
818 }
819
820 pub async fn get_guild_vanity_url(
822 &self,
823 guild_id: &str,
824 ) -> Result<GuildVanityUrl, ClientError> {
825 let url = format!("{}/guilds/{}/vanity-url", self.base_url, guild_id);
826 self.request_json(self.client.get(&url)).await
827 }
828
829 pub async fn update_guild_vanity_url(
831 &self,
832 guild_id: &str,
833 payload: &GuildVanityUrlUpdatePayload,
834 ) -> Result<GuildVanityUrlUpdateResponse, ClientError> {
835 let url = format!("{}/guilds/{}/vanity-url", self.base_url, guild_id);
836 self.request_json(self.client.patch(&url).json(payload)).await
837 }
838
839 pub async fn execute_webhook(
842 &self,
843 webhook_id: &str,
844 webhook_token: &str,
845 payload: &WebhookExecutePayload,
846 ) -> Result<Option<Message>, ClientError> {
847 let url = format!(
848 "{}/webhooks/{}/{}?wait=true",
849 self.base_url, webhook_id, webhook_token
850 );
851 self.request_json(self.client.post(&url).json(payload)).await
852 }
853
854 pub async fn get_webhook_message(
856 &self,
857 webhook_id: &str,
858 webhook_token: &str,
859 message_id: &str,
860 ) -> Result<Message, ClientError> {
861 let url = format!(
862 "{}/webhooks/{}/{}/messages/{}",
863 self.base_url, webhook_id, webhook_token, message_id
864 );
865 self.request_json(self.client.get(&url)).await
866 }
867
868 pub async fn edit_webhook_message(
870 &self,
871 webhook_id: &str,
872 webhook_token: &str,
873 message_id: &str,
874 payload: &WebhookEditPayload,
875 ) -> Result<Message, ClientError> {
876 let url = format!(
877 "{}/webhooks/{}/{}/messages/{}",
878 self.base_url, webhook_id, webhook_token, message_id
879 );
880 self.request_json(self.client.patch(&url).json(payload)).await
881 }
882}
883
884fn urlencoded(s: &str) -> String {
885 s.chars()
886 .flat_map(|c| {
887 let mut buf = [0u8; 4];
888 c.encode_utf8(&mut buf);
889 let bytes = &buf[..c.len_utf8()];
890 bytes
891 .iter()
892 .map(|b| match b {
893 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' | b':' => {
894 char::from(*b).to_string()
895 }
896 other => format!("%{:02X}", other),
897 })
898 .collect::<Vec<_>>()
899 })
900 .collect()
901}