Skip to main content

twapi_v2/
api.rs

1use std::time::Duration;
2
3use reqwest::RequestBuilder;
4use serde::de::DeserializeOwned;
5
6use crate::{
7    error::{Error, TwitterError},
8    headers::Headers,
9};
10
11pub mod delete_2_lists_id;
12pub mod delete_2_lists_id_members_user_id;
13pub mod delete_2_tweets_id;
14pub mod delete_2_users_id_bookmarks_tweet_id;
15pub mod delete_2_users_id_followed_lists_list_id;
16pub mod delete_2_users_id_likes_tweet_id;
17pub mod delete_2_users_id_pinned_lists;
18pub mod delete_2_users_id_retweets_source_tweet_id;
19pub mod delete_2_users_source_user_id_blocking_target_user_id;
20pub mod delete_2_users_source_user_id_following_target_user_id;
21pub mod delete_2_users_source_user_id_muting_target_user_id;
22pub mod get_2_compliance_jobs;
23pub mod get_2_compliance_jobs_id;
24pub mod get_2_dm_conversations_dm_conversation_id_dm_events;
25pub mod get_2_dm_conversations_with_participant_id_dm_events;
26pub mod get_2_dm_events;
27pub mod get_2_lists_id;
28pub mod get_2_lists_id_followers;
29pub mod get_2_lists_id_members;
30pub mod get_2_lists_id_tweets;
31pub mod get_2_media_upload;
32pub mod get_2_spaces;
33pub mod get_2_spaces_by_creator_ids;
34pub mod get_2_spaces_id;
35pub mod get_2_spaces_id_buyers;
36pub mod get_2_spaces_id_tweets;
37pub mod get_2_spaces_search;
38pub mod get_2_trends_by_woeid_woeid;
39pub mod get_2_tweets;
40pub mod get_2_tweets_compliance_stream;
41pub mod get_2_tweets_count_all;
42pub mod get_2_tweets_count_recent;
43pub mod get_2_tweets_id;
44pub mod get_2_tweets_id_liking_users;
45pub mod get_2_tweets_id_quote_tweets;
46pub mod get_2_tweets_id_retweeted_by;
47pub mod get_2_tweets_id_retweets;
48pub mod get_2_tweets_sample10_stream;
49pub mod get_2_tweets_sample_stream;
50pub mod get_2_tweets_search_all;
51pub mod get_2_tweets_search_recent;
52pub mod get_2_tweets_search_stream;
53pub mod get_2_tweets_search_stream_rules;
54pub mod get_2_usage_tweets;
55pub mod get_2_users;
56pub mod get_2_users_by;
57pub mod get_2_users_by_username_username;
58pub mod get_2_users_compliance_stream;
59pub mod get_2_users_id;
60pub mod get_2_users_id_blocking;
61pub mod get_2_users_id_bookmarks;
62pub mod get_2_users_id_followed_lists;
63pub mod get_2_users_id_followers;
64pub mod get_2_users_id_following;
65pub mod get_2_users_id_liked_tweets;
66pub mod get_2_users_id_list_memberships;
67pub mod get_2_users_id_mentions;
68pub mod get_2_users_id_muting;
69pub mod get_2_users_id_owned_lists;
70pub mod get_2_users_id_pinned_lists;
71pub mod get_2_users_id_timelines_reverse_chronological;
72pub mod get_2_users_id_tweets;
73pub mod get_2_users_me;
74pub mod get_2_users_reposts_of_me;
75pub mod get_2_users_search;
76pub mod post_2_compliance_jobs;
77pub mod post_2_dm_conversations;
78pub mod post_2_dm_conversations_dm_conversation_id_message;
79pub mod post_2_dm_conversations_with_participant_id_message;
80pub mod post_2_lists;
81pub mod post_2_lists_id_members;
82pub mod post_2_media_metadata_create;
83pub mod post_2_media_subtitles_create;
84pub mod post_2_media_subtitles_delete;
85pub mod post_2_media_upload_id_append;
86pub mod post_2_media_upload_id_finalize;
87pub mod post_2_media_upload_initialize;
88pub mod post_2_oauth2_token_refresh_token;
89pub mod post_2_tweets;
90pub mod post_2_tweets_search_stream_rules;
91pub mod post_2_users_id_blocking;
92pub mod post_2_users_id_bookmarks;
93pub mod post_2_users_id_followed_lists;
94pub mod post_2_users_id_following;
95pub mod post_2_users_id_likes;
96pub mod post_2_users_id_muting;
97pub mod post_2_users_id_pinned_lists;
98pub mod post_2_users_id_retweets;
99pub mod put_2_lists_id;
100pub mod put_2_tweets_id_hidden;
101
102const ENV_KEY: &str = "TWAPI_V2_TWITTER_API_PREFIX_API";
103const PREFIX_URL_TWITTER: &str = "https://api.x.com";
104
105pub fn clear_prefix_url() {
106    // TODO: Audit that the environment access only happens in single-threaded code.
107    unsafe { std::env::set_var(ENV_KEY, PREFIX_URL_TWITTER) };
108}
109
110pub fn setup_prefix_url(url: &str) {
111    // TODO: Audit that the environment access only happens in single-threaded code.
112    unsafe { std::env::set_var(ENV_KEY, url) };
113}
114
115#[derive(Debug, Clone, Default)]
116pub struct TwapiOptions {
117    pub prefix_url: Option<String>,
118    pub timeout: Option<Duration>,
119}
120
121pub(crate) fn make_url(twapi_options: &Option<TwapiOptions>, post_url: &str) -> String {
122    make_url_with_prefix(
123        &std::env::var(ENV_KEY).unwrap_or(PREFIX_URL_TWITTER.to_owned()),
124        twapi_options,
125        post_url,
126    )
127}
128
129pub(crate) fn make_url_with_prefix(
130    default_perfix_url: &str,
131    twapi_options: &Option<TwapiOptions>,
132    post_url: &str,
133) -> String {
134    let prefix_url = if let Some(twapi_options) = twapi_options {
135        if let Some(prefix_url) = twapi_options.prefix_url.as_ref() {
136            prefix_url
137        } else {
138            default_perfix_url
139        }
140    } else {
141        default_perfix_url
142    };
143    format!("{}{}", prefix_url, post_url)
144}
145
146pub trait Authentication {
147    fn execute(
148        &self,
149        builder: RequestBuilder,
150        method: &str,
151        uri: &str,
152        options: &[(&str, &str)],
153    ) -> RequestBuilder;
154}
155
156pub struct BearerAuthentication {
157    bearer_code: String,
158}
159
160impl BearerAuthentication {
161    pub fn new<T: Into<String>>(bearer_code: T) -> Self {
162        Self {
163            bearer_code: bearer_code.into(),
164        }
165    }
166}
167
168impl Authentication for BearerAuthentication {
169    fn execute(
170        &self,
171        builder: RequestBuilder,
172        _method: &str,
173        _uri: &str,
174        _options: &[(&str, &str)],
175    ) -> RequestBuilder {
176        builder.bearer_auth(&self.bearer_code)
177    }
178}
179
180pub async fn execute_twitter<T>(builder: RequestBuilder) -> Result<(T, Headers), Error>
181where
182    T: DeserializeOwned,
183{
184    let response = builder.send().await?;
185    let status_code = response.status();
186    let header = response.headers();
187    let headers = Headers::new(header);
188
189    println!("status_code: {:?}", status_code);
190
191    if status_code.is_success() {
192        Ok((response.json::<T>().await?, headers))
193    } else {
194        let text = response.text().await?;
195        println!("text: {:?}", text);
196        match serde_json::from_str(&text) {
197            Ok(value) => Err(Error::Twitter(
198                TwitterError::new(&value, status_code),
199                value,
200                Box::new(headers),
201            )),
202            Err(err) => Err(Error::Other(
203                format!("{:?}:{}", err, text),
204                Some(status_code),
205            )),
206        }
207    }
208}
209
210pub(crate) fn apply_options(
211    client: RequestBuilder,
212    options: &Option<TwapiOptions>,
213) -> RequestBuilder {
214    let Some(options) = options else {
215        return client;
216    };
217    let Some(timeout) = options.timeout else {
218        return client;
219    };
220    client.timeout(timeout)
221}