kick-api 0.1.9

Rust client for the Kick.com API
Documentation
use crate::error::{KickApiError, Result};
use crate::models::{Channel, UpdateChannelRequest};
use reqwest;

/// Channels API - handles all channel-related endpoints
pub struct ChannelsApi<'a> {
    client: &'a reqwest::Client,
    token: &'a Option<String>,
    base_url: &'a str,
}

impl<'a> ChannelsApi<'a> {
    /// Create a new ChannelsApi instance
    pub(crate) fn new(
        client: &'a reqwest::Client,
        token: &'a Option<String>,
        base_url: &'a str,
    ) -> Self {
        Self {
            client,
            token,
            base_url,
        }
    }

    /// Get a channel by slug
    ///
    /// Requires OAuth token with `channel:read` scope
    ///
    /// # Example
    /// ```no_run
    /// let channel = client.channels().get("xqc").await?;
    /// println!("Channel: {}", channel.slug);
    /// ```
    pub async fn get(&self, channel_slug: &str) -> Result<Channel> {
        super::require_token(self.token)?;

        let url = format!("{}/channels", self.base_url);
        let request = self
            .client
            .get(&url)
            .header("Accept", "*/*")
            .query(&[("slug", channel_slug)])
            .bearer_auth(self.token.as_ref().unwrap());

        let response = crate::http::send_with_retry(self.client, request).await?;
        if response.status().is_success() {
            let body = response.text().await?;

            #[derive(serde::Deserialize)]
            struct ChannelsResponse {
                data: Vec<Channel>,
            }

            let resp: ChannelsResponse = serde_json::from_str(&body)
                .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;

            resp.data
                .into_iter()
                .next()
                .ok_or_else(|| KickApiError::ApiError("Channel not found".to_string()))
        } else {
            Err(KickApiError::ApiError(format!(
                "Failed to get channel: {}",
                response.status()
            )))
        }
    }

    /// Update channel/livestream metadata
    ///
    /// Requires OAuth token with `channel:write` scope.
    /// At least one field in the request must be set.
    ///
    /// # Example
    /// ```no_run
    /// use kick_api::UpdateChannelRequest;
    /// let update = UpdateChannelRequest {
    ///     stream_title: Some("New title!".to_string()),
    ///     category_id: None,
    ///     custom_tags: Some(vec!["rust".to_string()]),
    /// };
    /// client.channels().update(update).await?;
    /// ```
    pub async fn update(&self, request: UpdateChannelRequest) -> Result<()> {
        super::require_token(self.token)?;

        let url = format!("{}/channels", self.base_url);
        let req = self
            .client
            .patch(&url)
            .header("Accept", "*/*")
            .bearer_auth(self.token.as_ref().unwrap())
            .json(&request);

        let response = crate::http::send_with_retry(self.client, req).await?;
        let status = response.status();
        if status.is_success() {
            Ok(())
        } else {
            let body = response.text().await.unwrap_or_default();
            Err(KickApiError::ApiError(format!(
                "Failed to update channel: {} - {}",
                status, body
            )))
        }
    }

    /// Get your own channels (the authenticated user's channels)
    ///
    /// Requires OAuth token with `channel:read` scope
    ///
    /// # Example
    /// ```no_run
    /// let my_channels = client.channels().get_mine().await?;
    /// for channel in my_channels {
    ///     println!("My channel: {}", channel.slug);
    /// }
    /// ```
    pub async fn get_mine(&self) -> Result<Vec<Channel>> {
        super::require_token(self.token)?;

        let url = format!("{}/channels", self.base_url);
        let request = self
            .client
            .get(&url)
            .header("Accept", "*/*")
            .bearer_auth(self.token.as_ref().unwrap());

        let response = crate::http::send_with_retry(self.client, request).await?;
        if response.status().is_success() {
            let body = response.text().await?;

            #[derive(serde::Deserialize)]
            struct ChannelsResponse {
                data: Vec<Channel>,
            }

            let resp: ChannelsResponse = serde_json::from_str(&body)
                .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;

            Ok(resp.data)
        } else {
            Err(KickApiError::ApiError(format!(
                "Failed to get channels: {}",
                response.status()
            )))
        }
    }
}