titanium_http/
guild.rs

1use crate::error::HttpError;
2use crate::HttpClient;
3use serde::{Deserialize, Serialize};
4use titanium_model::{
5    AuditLogEntry, AutoModRule, Channel, GuildMember, Integration, Role, ScheduledEvent, Snowflake,
6    SoundboardSound, User, Webhook,
7};
8
9// ============================================================================
10// Audit Log Response (merged from audit_log.rs)
11// ============================================================================
12
13/// Response structure for Get Guild Audit Log.
14#[derive(Debug, Deserialize, Serialize)]
15pub struct AuditLog<'a> {
16    pub audit_log_entries: Vec<AuditLogEntry>,
17    pub auto_moderation_rules: Vec<AutoModRule>,
18    pub guild_scheduled_events: Vec<ScheduledEvent<'a>>,
19    pub integrations: Vec<Integration<'a>>,
20    pub threads: Vec<Channel<'a>>,
21    pub users: Vec<User<'a>>,
22    pub webhooks: Vec<Webhook<'a>>,
23}
24
25#[derive(Debug, Default, Serialize)]
26pub struct GetAuditLogParams {
27    pub user_id: Option<Snowflake>,
28    pub action_type: Option<u8>,
29    pub before: Option<Snowflake>,
30    pub after: Option<Snowflake>,
31    pub limit: Option<u32>,
32}
33
34impl HttpClient {
35    // =========================================================================
36    // Guild Member Operations
37    // =========================================================================
38
39    /// Get a guild member.
40    pub async fn get_member(
41        &self,
42        guild_id: Snowflake,
43        user_id: Snowflake,
44    ) -> Result<GuildMember<'static>, HttpError> {
45        let route = format!("/guilds/{}/members/{}", guild_id, user_id);
46        self.get(&route).await
47    }
48
49    /// List guild members.
50    pub async fn list_members(
51        &self,
52        guild_id: Snowflake,
53        limit: Option<u32>,
54        after: Option<Snowflake>,
55    ) -> Result<Vec<GuildMember<'static>>, HttpError> {
56        #[derive(Serialize)]
57        struct Query {
58            limit: u32,
59            after: Option<Snowflake>,
60        }
61
62        let query = Query {
63            limit: limit.unwrap_or(1),
64            after,
65        };
66
67        let route = format!("/guilds/{}/members", guild_id);
68        self.get_with_query(&route, &query).await
69    }
70
71    /// Kick a member from the guild.
72    pub async fn kick_member(
73        &self,
74        guild_id: Snowflake,
75        user_id: Snowflake,
76        reason: Option<&str>,
77    ) -> Result<(), HttpError> {
78        let route = format!("/guilds/{}/members/{}", guild_id, user_id);
79
80        let headers = reason
81            .map(|r| -> Result<_, HttpError> {
82                let mut h = reqwest::header::HeaderMap::new();
83                h.insert(
84                    "X-Audit-Log-Reason",
85                    reqwest::header::HeaderValue::from_str(r)?,
86                );
87                Ok(h)
88            })
89            .transpose()?;
90
91        self.delete_with_headers(&route, headers).await
92    }
93
94    /// Ban a member from the guild.
95    pub async fn ban_member(
96        &self,
97        guild_id: Snowflake,
98        user_id: Snowflake,
99        delete_message_seconds: Option<u32>,
100        reason: Option<&str>,
101    ) -> Result<(), HttpError> {
102        #[derive(Serialize)]
103        struct BanBody {
104            delete_message_seconds: Option<u32>,
105        }
106
107        let body = BanBody {
108            delete_message_seconds,
109        };
110        let route = format!("/guilds/{}/bans/{}", guild_id, user_id);
111
112        let headers = reason.and_then(|r| {
113            reqwest::header::HeaderValue::from_str(r).ok().map(|v| {
114                let mut h = reqwest::header::HeaderMap::new();
115                h.insert("X-Audit-Log-Reason", v);
116                h
117            })
118        });
119
120        self.put_with_headers(&route, Some(body), headers).await
121    }
122
123    /// Unban a member.
124    pub async fn unban_member(
125        &self,
126        guild_id: Snowflake,
127        user_id: Snowflake,
128        reason: Option<&str>,
129    ) -> Result<(), HttpError> {
130        let route = format!("/guilds/{}/bans/{}", guild_id, user_id);
131
132        let headers = reason.and_then(|r| {
133            reqwest::header::HeaderValue::from_str(r).ok().map(|v| {
134                let mut h = reqwest::header::HeaderMap::new();
135                h.insert("X-Audit-Log-Reason", v);
136                h
137            })
138        });
139
140        self.delete_with_headers(&route, headers).await
141    }
142
143    /// Modify a guild member.
144    pub async fn modify_member(
145        &self,
146        guild_id: Snowflake,
147        user_id: Snowflake,
148        params: &titanium_model::builder::ModifyMember<'_>,
149    ) -> Result<GuildMember<'static>, HttpError> {
150        let route = format!("/guilds/{}/members/{}", guild_id, user_id);
151        self.patch(&route, params).await
152    }
153
154    // =========================================================================
155    // Role Operations
156    // =========================================================================
157
158    /// Get all roles.
159    pub async fn get_roles(&self, guild_id: Snowflake) -> Result<Vec<Role<'static>>, HttpError> {
160        let route = format!("/guilds/{}/roles", guild_id);
161        self.get(&route).await
162    }
163
164    /// Create a new role.
165    pub async fn create_role(
166        &self,
167        guild_id: Snowflake,
168        params: &titanium_model::builder::CreateRole<'_>,
169    ) -> Result<Role<'static>, HttpError> {
170        let route = format!("/guilds/{}/roles", guild_id);
171        self.post(&route, params).await
172    }
173
174    /// Delete a role.
175    pub async fn delete_role(
176        &self,
177        guild_id: Snowflake,
178        role_id: Snowflake,
179        reason: Option<&str>,
180    ) -> Result<(), HttpError> {
181        let route = format!("/guilds/{}/roles/{}", guild_id, role_id);
182
183        let headers = reason
184            .map(|r| -> Result<_, HttpError> {
185                let mut h = reqwest::header::HeaderMap::new();
186                h.insert(
187                    "X-Audit-Log-Reason",
188                    reqwest::header::HeaderValue::from_str(r)?,
189                );
190                Ok(h)
191            })
192            .transpose()?;
193
194        self.delete_with_headers(&route, headers).await
195    }
196
197    // =========================================================================
198    // Audit Log (merged from audit_log.rs)
199    // =========================================================================
200
201    /// Get guild audit log.
202    pub async fn get_guild_audit_log(
203        &self,
204        guild_id: Snowflake,
205        params: &GetAuditLogParams,
206    ) -> Result<AuditLog<'static>, HttpError> {
207        let route = format!("/guilds/{}/audit-logs", guild_id);
208        self.get_with_query(&route, params).await
209    }
210
211    // =========================================================================
212    // Soundboard (merged from soundboard.rs)
213    // =========================================================================
214
215    /// List soundboard sounds for a guild.
216    pub async fn list_guild_soundboard_sounds(
217        &self,
218        guild_id: Snowflake,
219    ) -> Result<Vec<SoundboardSound<'static>>, HttpError> {
220        self.get(&format!("/guilds/{}/soundboard-sounds", guild_id))
221            .await
222    }
223
224    /// Get a specific soundboard sound.
225    pub async fn get_guild_soundboard_sound(
226        &self,
227        guild_id: Snowflake,
228        sound_id: Snowflake,
229    ) -> Result<SoundboardSound<'static>, HttpError> {
230        self.get(&format!(
231            "/guilds/{}/soundboard-sounds/{}",
232            guild_id, sound_id
233        ))
234        .await
235    }
236
237    /// Create a soundboard sound.
238    pub async fn create_guild_soundboard_sound(
239        &self,
240        guild_id: Snowflake,
241        payload: &serde_json::Value,
242    ) -> Result<SoundboardSound<'static>, HttpError> {
243        self.post(&format!("/guilds/{}/soundboard-sounds", guild_id), payload)
244            .await
245    }
246
247    /// Modify a soundboard sound.
248    pub async fn modify_guild_soundboard_sound(
249        &self,
250        guild_id: Snowflake,
251        sound_id: Snowflake,
252        payload: &serde_json::Value,
253    ) -> Result<SoundboardSound<'static>, HttpError> {
254        self.patch(
255            &format!("/guilds/{}/soundboard-sounds/{}", guild_id, sound_id),
256            payload,
257        )
258        .await
259    }
260
261    /// Delete a soundboard sound.
262    pub async fn delete_guild_soundboard_sound(
263        &self,
264        guild_id: Snowflake,
265        sound_id: Snowflake,
266    ) -> Result<(), HttpError> {
267        self.delete(&format!(
268            "/guilds/{}/soundboard-sounds/{}",
269            guild_id, sound_id
270        ))
271        .await
272    }
273}