Skip to main content

kick_api/api/
events.rs

1use crate::error::{KickApiError, Result};
2use crate::models::{EventSubscription, SubscribeRequest, SubscribeResult};
3use reqwest;
4
5/// Events API - handles webhook/event subscription endpoints
6///
7/// Scopes required: `events:subscribe`
8pub struct EventsApi<'a> {
9    client: &'a reqwest::Client,
10    token: &'a Option<String>,
11    base_url: &'a str,
12}
13
14impl<'a> EventsApi<'a> {
15    /// Create a new EventsApi instance
16    pub(crate) fn new(
17        client: &'a reqwest::Client,
18        token: &'a Option<String>,
19        base_url: &'a str,
20    ) -> Self {
21        Self {
22            client,
23            token,
24            base_url,
25        }
26    }
27
28    /// List active event subscriptions
29    ///
30    /// Optionally filter by broadcaster user ID.
31    ///
32    /// Requires OAuth token with `events:subscribe` scope
33    ///
34    /// # Example
35    /// ```no_run
36    /// // List all subscriptions
37    /// let subs = client.events().list(None).await?;
38    ///
39    /// // List subscriptions for a specific broadcaster
40    /// let subs = client.events().list(Some(12345)).await?;
41    /// ```
42    pub async fn list(
43        &self,
44        broadcaster_user_id: Option<u64>,
45    ) -> Result<Vec<EventSubscription>> {
46        self.require_token()?;
47
48        let url = format!("{}/events/subscriptions", self.base_url);
49        let mut request = self
50            .client
51            .get(&url)
52            .header("Accept", "*/*")
53            .bearer_auth(self.token.as_ref().unwrap());
54
55        if let Some(id) = broadcaster_user_id {
56            request = request.query(&[("broadcaster_user_id", id)]);
57        }
58
59        let response = crate::http::send_with_retry(self.client, request).await?;
60
61        if response.status().is_success() {
62            let body = response.text().await?;
63
64            #[derive(serde::Deserialize)]
65            struct DataResponse {
66                data: Vec<EventSubscription>,
67            }
68
69            let resp: DataResponse = serde_json::from_str(&body)
70                .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
71
72            Ok(resp.data)
73        } else {
74            Err(KickApiError::ApiError(format!(
75                "Failed to list event subscriptions: {}",
76                response.status()
77            )))
78        }
79    }
80
81    /// Subscribe to events
82    ///
83    /// Requires OAuth token with `events:subscribe` scope
84    ///
85    /// # Example
86    /// ```no_run
87    /// use kick_api::{SubscribeRequest, SubscribeEvent};
88    ///
89    /// let request = SubscribeRequest {
90    ///     broadcaster_user_id: Some(12345),
91    ///     method: "webhook".to_string(),
92    ///     events: vec![
93    ///         SubscribeEvent { name: "chat.message.created".to_string(), version: 1 },
94    ///     ],
95    /// };
96    /// let results = client.events().subscribe(request).await?;
97    /// ```
98    pub async fn subscribe(
99        &self,
100        request: SubscribeRequest,
101    ) -> Result<Vec<SubscribeResult>> {
102        self.require_token()?;
103
104        let url = format!("{}/events/subscriptions", self.base_url);
105        let request = self
106            .client
107            .post(&url)
108            .header("Accept", "*/*")
109            .bearer_auth(self.token.as_ref().unwrap())
110            .json(&request);
111        let response = crate::http::send_with_retry(self.client, request).await?;
112
113        if response.status().is_success() {
114            let body = response.text().await?;
115
116            #[derive(serde::Deserialize)]
117            struct DataResponse {
118                data: Vec<SubscribeResult>,
119            }
120
121            let resp: DataResponse = serde_json::from_str(&body)
122                .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
123
124            Ok(resp.data)
125        } else {
126            Err(KickApiError::ApiError(format!(
127                "Failed to subscribe to events: {}",
128                response.status()
129            )))
130        }
131    }
132
133    /// Unsubscribe from events by subscription IDs
134    ///
135    /// Requires OAuth token with `events:subscribe` scope
136    ///
137    /// # Example
138    /// ```no_run
139    /// client.events().unsubscribe(vec!["sub_id_1".to_string(), "sub_id_2".to_string()]).await?;
140    /// ```
141    pub async fn unsubscribe(&self, ids: Vec<String>) -> Result<()> {
142        self.require_token()?;
143
144        let url = format!("{}/events/subscriptions", self.base_url);
145        let id_pairs: Vec<(&str, &str)> = ids.iter().map(|id| ("id", id.as_str())).collect();
146
147        let request = self
148            .client
149            .delete(&url)
150            .header("Accept", "*/*")
151            .bearer_auth(self.token.as_ref().unwrap())
152            .query(&id_pairs);
153        let response = crate::http::send_with_retry(self.client, request).await?;
154
155        if response.status().is_success() {
156            Ok(())
157        } else {
158            Err(KickApiError::ApiError(format!(
159                "Failed to unsubscribe from events: {}",
160                response.status()
161            )))
162        }
163    }
164
165    fn require_token(&self) -> Result<()> {
166        if self.token.is_none() {
167            return Err(KickApiError::ApiError(
168                "OAuth token required for this endpoint".to_string(),
169            ));
170        }
171        Ok(())
172    }
173}