agent_twitter_client/api/
client.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use crate::auth::user_auth::TwitterAuth;
use crate::error::{Result, TwitterError};
use crate::models::Tweet;
use reqwest::{Client, Method};
use serde::de::DeserializeOwned;
use std::time::Duration;

pub struct TwitterApiClient {
    pub client: Client,
    pub auth: Box<dyn TwitterAuth + Send + Sync>,
}

impl TwitterApiClient {
    pub fn new(auth: Box<dyn TwitterAuth + Send + Sync>) -> Result<Self> {
        let client = Client::builder()
            .timeout(Duration::from_secs(30))
            .cookie_store(true)
            .build()?;

        Ok(Self { client, auth })
    }

    pub async fn send_tweet(&self, text: &str, media_ids: Option<Vec<String>>) -> Result<Tweet> {
        let mut params = serde_json::json!({
            "text": text,
        });

        if let Some(ids) = media_ids {
            params["media"] = serde_json::json!({ "media_ids": ids });
        }

        let endpoint = "https://api.twitter.com/2/tweets";
        self.post(endpoint, Some(params)).await
    }

    pub async fn get_tweet(&self, tweet_id: &str) -> Result<Tweet> {
        let endpoint = format!("https://api.twitter.com/2/tweets/{}", tweet_id);
        self.get(&endpoint).await
    }

    pub async fn get_user_tweets(&self, user_id: &str, limit: usize) -> Result<Vec<Tweet>> {
        let endpoint = format!("https://api.twitter.com/2/users/{}/tweets", user_id);
        let params = serde_json::json!({
            "max_results": limit,
            "tweet.fields": "created_at,author_id,conversation_id,public_metrics"
        });
        self.get_with_params(&endpoint, Some(params)).await
    }

    pub async fn get<T: DeserializeOwned>(&self, endpoint: &str) -> Result<T> {
        self.request(Method::GET, endpoint, None).await
    }

    pub async fn get_with_params<T: DeserializeOwned>(
        &self,
        endpoint: &str,
        params: Option<serde_json::Value>,
    ) -> Result<T> {
        self.request(Method::GET, endpoint, params).await
    }

    pub async fn post<T: DeserializeOwned>(
        &self,
        endpoint: &str,
        params: Option<serde_json::Value>,
    ) -> Result<T> {
        self.request(Method::POST, endpoint, params).await
    }

    pub async fn request<T: DeserializeOwned>(
        &self,
        method: Method,
        endpoint: &str,
        params: Option<serde_json::Value>,
    ) -> Result<T> {
        let mut headers = reqwest::header::HeaderMap::new();
        self.auth.install_headers(&mut headers).await?;

        let mut request = self.client.request(method, endpoint);
        request = request.headers(headers);

        if let Some(params) = params {
            request = request.json(&params);
        }

        let response = request.send().await?;

        if response.status().is_success() {
            Ok(response.json().await?)
        } else {
            Err(TwitterError::Api(format!(
                "Request failed with status: {}",
                response.status()
            )))
        }
    }
}