Skip to main content

twapi_v2/api/
get_2_tweets_search_all.rs

1use crate::fields::{
2    media_fields::MediaFields, place_fields::PlaceFields, poll_fields::PollFields,
3    tweet_fields::TweetFields, user_fields::UserFields,
4};
5use crate::responses::{errors::Errors, includes::Includes, meta::Meta, tweets::Tweets};
6use crate::{
7    api::{Authentication, TwapiOptions, execute_twitter, make_url},
8    error::Error,
9    headers::Headers,
10};
11use chrono::prelude::*;
12use itertools::Itertools;
13use reqwest::RequestBuilder;
14use serde::{Deserialize, Serialize};
15use std::collections::HashSet;
16
17const URL: &str = "/2/tweets/search/all";
18
19#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Clone)]
20#[derive(Default)]
21pub enum Expansions {
22    #[serde(rename = "article.cover_media")]
23    #[default]
24    ArticleCoverMedia,
25    #[serde(rename = "article.media_entities")]
26    ArticleMediaEntities,
27    #[serde(rename = "attachments.media_keys")]
28    AttachmentsMediaKeys,
29    #[serde(rename = "attachments.media_source_tweet")]
30    AttachmentsMediaSourceTweet,
31    #[serde(rename = "attachments.poll_ids")]
32    AttachmentsPollIds,
33    #[serde(rename = "author_id")]
34    AuthorId,
35    #[serde(rename = "edit_history_tweet_ids")]
36    EditHistoryTweetIds,
37    #[serde(rename = "entities.mentions.username")]
38    EntitiesMentionsUsername,
39    #[serde(rename = "geo.place_id")]
40    GeoPlaceId,
41    #[serde(rename = "in_reply_to_user_id")]
42    InReplyToUserId,
43    #[serde(rename = "entities.note.mentions.username")]
44    EntitiesNoteMentionsUsername,
45    #[serde(rename = "referenced_tweets.id")]
46    ReferencedTweetsId,
47    #[serde(rename = "referenced_tweets.id.author_id")]
48    ReferencedTweetsIdAuthorId,
49}
50
51impl Expansions {
52    pub fn all() -> HashSet<Self> {
53        let mut result = HashSet::new();
54        result.insert(Self::ArticleCoverMedia);
55        result.insert(Self::ArticleMediaEntities);
56        result.insert(Self::AttachmentsMediaKeys);
57        result.insert(Self::AttachmentsMediaSourceTweet);
58        result.insert(Self::AttachmentsPollIds);
59        result.insert(Self::AuthorId);
60        result.insert(Self::EditHistoryTweetIds);
61        result.insert(Self::EntitiesMentionsUsername);
62        result.insert(Self::GeoPlaceId);
63        result.insert(Self::InReplyToUserId);
64        result.insert(Self::EntitiesNoteMentionsUsername);
65        result.insert(Self::ReferencedTweetsId);
66        result.insert(Self::ReferencedTweetsIdAuthorId);
67        result
68    }
69}
70
71impl std::fmt::Display for Expansions {
72    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73        match self {
74            Self::ArticleCoverMedia => write!(f, "article.cover_media"),
75            Self::ArticleMediaEntities => write!(f, "article.media_entities"),
76            Self::AttachmentsMediaKeys => write!(f, "attachments.media_keys"),
77            Self::AttachmentsMediaSourceTweet => write!(f, "attachments.media_source_tweet"),
78            Self::AttachmentsPollIds => write!(f, "attachments.poll_ids"),
79            Self::AuthorId => write!(f, "author_id"),
80            Self::EditHistoryTweetIds => write!(f, "edit_history_tweet_ids"),
81            Self::EntitiesMentionsUsername => write!(f, "entities.mentions.username"),
82            Self::GeoPlaceId => write!(f, "geo.place_id"),
83            Self::InReplyToUserId => write!(f, "in_reply_to_user_id"),
84            Self::EntitiesNoteMentionsUsername => write!(f, "entities.note.mentions.username"),
85            Self::ReferencedTweetsId => write!(f, "referenced_tweets.id"),
86            Self::ReferencedTweetsIdAuthorId => write!(f, "referenced_tweets.id.author_id"),
87        }
88    }
89}
90
91
92#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Clone)]
93#[derive(Default)]
94pub enum SortOrder {
95    #[serde(rename = "recency")]
96    #[default]
97    Recency,
98    #[serde(rename = "relevancy")]
99    Relevancy,
100}
101
102impl std::fmt::Display for SortOrder {
103    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
104        match self {
105            Self::Recency => write!(f, "recency"),
106            Self::Relevancy => write!(f, "relevancy"),
107        }
108    }
109}
110
111
112#[derive(Debug, Clone, Default)]
113pub struct Api {
114    query: String,
115    end_time: Option<DateTime<Utc>>,
116    expansions: Option<HashSet<Expansions>>,
117    max_results: Option<usize>,
118    media_fields: Option<HashSet<MediaFields>>,
119    next_token: Option<String>,
120    place_fields: Option<HashSet<PlaceFields>>,
121    poll_fields: Option<HashSet<PollFields>>,
122    since_id: Option<String>,
123    sort_order: Option<SortOrder>,
124    start_time: Option<DateTime<Utc>>,
125    tweet_fields: Option<HashSet<TweetFields>>,
126    until_id: Option<String>,
127    user_fields: Option<HashSet<UserFields>>,
128    twapi_options: Option<TwapiOptions>,
129}
130
131impl Api {
132    pub fn new(query: &str) -> Self {
133        Self {
134            query: query.to_owned(),
135            ..Default::default()
136        }
137    }
138
139    pub fn all(query: &str) -> Self {
140        Self {
141            query: query.to_owned(),
142            expansions: Some(Expansions::all()),
143            media_fields: Some(MediaFields::all()),
144            place_fields: Some(PlaceFields::all()),
145            poll_fields: Some(PollFields::all()),
146            tweet_fields: Some(TweetFields::organic()),
147            user_fields: Some(UserFields::all()),
148            max_results: Some(500),
149            ..Default::default()
150        }
151    }
152
153    pub fn open(query: &str) -> Self {
154        Self {
155            query: query.to_owned(),
156            expansions: Some(Expansions::all()),
157            media_fields: Some(MediaFields::open()),
158            place_fields: Some(PlaceFields::all()),
159            poll_fields: Some(PollFields::all()),
160            tweet_fields: Some(TweetFields::open()),
161            user_fields: Some(UserFields::all()),
162            max_results: Some(500),
163            ..Default::default()
164        }
165    }
166
167    pub fn end_time(mut self, value: DateTime<Utc>) -> Self {
168        self.end_time = Some(value);
169        self
170    }
171
172    pub fn expansions(mut self, value: HashSet<Expansions>) -> Self {
173        self.expansions = Some(value);
174        self
175    }
176
177    pub fn max_results(mut self, value: usize) -> Self {
178        self.max_results = Some(value);
179        self
180    }
181
182    pub fn media_fields(mut self, value: HashSet<MediaFields>) -> Self {
183        self.media_fields = Some(value);
184        self
185    }
186
187    pub fn next_token(mut self, value: &str) -> Self {
188        self.next_token = Some(value.to_owned());
189        self
190    }
191
192    pub fn place_fields(mut self, value: HashSet<PlaceFields>) -> Self {
193        self.place_fields = Some(value);
194        self
195    }
196
197    pub fn poll_fields(mut self, value: HashSet<PollFields>) -> Self {
198        self.poll_fields = Some(value);
199        self
200    }
201
202    pub fn since_id(mut self, value: &str) -> Self {
203        self.since_id = Some(value.to_owned());
204        self
205    }
206
207    pub fn sort_order(mut self, value: SortOrder) -> Self {
208        self.sort_order = Some(value);
209        self
210    }
211
212    pub fn start_time(mut self, value: DateTime<Utc>) -> Self {
213        self.start_time = Some(value);
214        self
215    }
216
217    pub fn tweet_fields(mut self, value: HashSet<TweetFields>) -> Self {
218        self.tweet_fields = Some(value);
219        self
220    }
221
222    pub fn until_id(mut self, value: &str) -> Self {
223        self.until_id = Some(value.to_owned());
224        self
225    }
226
227    pub fn user_fields(mut self, value: HashSet<UserFields>) -> Self {
228        self.user_fields = Some(value);
229        self
230    }
231
232    pub fn twapi_options(mut self, value: TwapiOptions) -> Self {
233        self.twapi_options = Some(value);
234        self
235    }
236
237    pub fn build(&self, authentication: &impl Authentication) -> RequestBuilder {
238        let mut query_parameters = vec![];
239        query_parameters.push(("query", self.query.to_string()));
240        if let Some(end_time) = self.end_time.as_ref() {
241            query_parameters.push((
242                "end_time",
243                end_time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
244            ));
245        }
246        if let Some(expansions) = self.expansions.as_ref() {
247            query_parameters.push(("expansions", expansions.iter().join(",")));
248        }
249        if let Some(max_results) = self.max_results.as_ref() {
250            query_parameters.push(("max_results", max_results.to_string()));
251        }
252        if let Some(media_fields) = self.media_fields.as_ref() {
253            query_parameters.push(("media.fields", media_fields.iter().join(",")));
254        }
255        if let Some(next_token) = self.next_token.as_ref() {
256            query_parameters.push(("next_token", next_token.to_string()));
257        }
258        if let Some(place_fields) = self.place_fields.as_ref() {
259            query_parameters.push(("place.fields", place_fields.iter().join(",")));
260        }
261        if let Some(poll_fields) = self.poll_fields.as_ref() {
262            query_parameters.push(("poll.fields", poll_fields.iter().join(",")));
263        }
264        if let Some(since_id) = self.since_id.as_ref() {
265            query_parameters.push(("since_id", since_id.to_string()));
266        }
267        if let Some(sort_order) = self.sort_order.as_ref() {
268            query_parameters.push(("sort_order", sort_order.to_string()));
269        }
270        if let Some(start_time) = self.start_time.as_ref() {
271            query_parameters.push((
272                "start_time",
273                start_time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
274            ));
275        }
276        if let Some(tweet_fields) = self.tweet_fields.as_ref() {
277            query_parameters.push(("tweet.fields", tweet_fields.iter().join(",")));
278        }
279        if let Some(until_id) = self.until_id.as_ref() {
280            query_parameters.push(("until_id", until_id.to_string()));
281        }
282        if let Some(user_fields) = self.user_fields.as_ref() {
283            query_parameters.push(("user.fields", user_fields.iter().join(",")));
284        }
285        let client = reqwest::Client::new();
286        let url = make_url(&self.twapi_options, URL);
287        let builder = client.get(&url).query(&query_parameters);
288        authentication.execute(
289            builder,
290            "GET",
291            &url,
292            &query_parameters
293                .iter()
294                .map(|it| (it.0, it.1.as_str()))
295                .collect::<Vec<_>>(),
296        )
297    }
298
299    pub async fn execute(
300        &self,
301        authentication: &impl Authentication,
302    ) -> Result<(Response, Headers), Error> {
303        execute_twitter(|| self.build(authentication), &self.twapi_options).await
304    }
305}
306
307#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
308pub struct Response {
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub data: Option<Vec<Tweets>>,
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub errors: Option<Vec<Errors>>,
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub includes: Option<Includes>,
315    #[serde(skip_serializing_if = "Option::is_none")]
316    pub meta: Option<Meta>,
317    #[serde(flatten)]
318    pub extra: std::collections::HashMap<String, serde_json::Value>,
319}
320
321impl Response {
322    pub fn is_empty_extra(&self) -> bool {
323        let res = self.extra.is_empty()
324            && self
325                .data
326                .as_ref()
327                .map(|it| it.iter().all(|item| item.is_empty_extra()))
328                .unwrap_or(true)
329            && self
330                .errors
331                .as_ref()
332                .map(|it| it.iter().all(|item| item.is_empty_extra()))
333                .unwrap_or(true)
334            && self
335                .includes
336                .as_ref()
337                .map(|it| it.is_empty_extra())
338                .unwrap_or(true)
339            && self
340                .meta
341                .as_ref()
342                .map(|it| it.is_empty_extra())
343                .unwrap_or(true);
344        if !res {
345            println!("Response {:?}", self.extra);
346        }
347        res
348    }
349}