twapi_v2/api/
post_2_tweets.rs

1use crate::responses::errors::Errors;
2use crate::{
3    api::{apply_options, execute_twitter, make_url, Authentication, TwapiOptions},
4    error::Error,
5    headers::Headers,
6};
7use reqwest::RequestBuilder;
8use serde::{Deserialize, Serialize};
9
10const URL: &str = "/2/tweets";
11
12#[derive(Serialize, Deserialize, Debug, Default, Clone)]
13pub struct Geo {
14    pub place_id: String,
15}
16
17#[derive(Serialize, Deserialize, Debug, Default, Clone)]
18pub struct Media {
19    pub media_ids: Vec<String>,
20    pub tagged_user_ids: Vec<String>,
21}
22
23#[derive(Serialize, Deserialize, Debug, Default, Clone)]
24pub struct Poll {
25    pub duration_minutes: String,
26    pub options: Vec<String>,
27}
28
29#[derive(Serialize, Deserialize, Debug, Default, Clone)]
30pub struct Reply {
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub exclude_reply_user_ids: Option<Vec<String>>,
33    pub in_reply_to_tweet_id: String,
34}
35
36#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Clone)]
37pub enum ReplySettings {
38    #[serde(rename = "mentionedUsers")]
39    Mentionedusers,
40    #[serde(rename = "following")]
41    Following,
42}
43
44impl std::fmt::Display for ReplySettings {
45    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
46        match self {
47            Self::Mentionedusers => write!(f, "mentionedUsers"),
48            Self::Following => write!(f, "following"),
49        }
50    }
51}
52
53impl Default for ReplySettings {
54    fn default() -> Self {
55        Self::Mentionedusers
56    }
57}
58
59#[derive(Serialize, Deserialize, Debug, Default, Clone)]
60pub struct Body {
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub direct_message_deep_link: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub for_super_followers_only: Option<bool>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub geo: Option<Geo>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub media: Option<Media>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub poll: Option<Poll>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub quote_tweet_id: Option<String>,
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub reply: Option<Reply>,
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub reply_settings: Option<ReplySettings>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub text: Option<String>,
79}
80
81#[derive(Debug, Clone, Default)]
82pub struct Api {
83    body: Body,
84    twapi_options: Option<TwapiOptions>,
85}
86
87impl Api {
88    pub fn new(body: Body) -> Self {
89        Self {
90            body,
91            ..Default::default()
92        }
93    }
94
95    pub fn twapi_options(mut self, value: TwapiOptions) -> Self {
96        self.twapi_options = Some(value);
97        self
98    }
99
100    pub fn build(self, authentication: &impl Authentication) -> RequestBuilder {
101        let client = reqwest::Client::new();
102        let url = make_url(&self.twapi_options, URL);
103        let builder = client.post(&url).json(&self.body);
104        authentication.execute(
105            apply_options(builder, &self.twapi_options),
106            "POST",
107            &url,
108            &[],
109        )
110    }
111
112    pub async fn execute(
113        self,
114        authentication: &impl Authentication,
115    ) -> Result<(Response, Headers), Error> {
116        execute_twitter(self.build(authentication)).await
117    }
118}
119
120#[derive(Serialize, Deserialize, Debug, Clone, Default)]
121pub struct Response {
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub data: Option<Data>,
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub errors: Option<Vec<Errors>>,
126    #[serde(flatten)]
127    pub extra: std::collections::HashMap<String, serde_json::Value>,
128}
129
130impl Response {
131    pub fn is_empty_extra(&self) -> bool {
132        let res = self.extra.is_empty()
133            && self
134                .data
135                .as_ref()
136                .map(|it| it.is_empty_extra())
137                .unwrap_or(true)
138            && self
139                .errors
140                .as_ref()
141                .map(|it| it.iter().all(|item| item.is_empty_extra()))
142                .unwrap_or(true);
143        if !res {
144            println!("Response {:?}", self.extra);
145        }
146        res
147    }
148}
149
150#[derive(Serialize, Deserialize, Debug, Clone, Default)]
151pub struct Data {
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub id: Option<String>,
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub text: Option<String>,
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub edit_history_tweet_ids: Option<Vec<String>>,
158    #[serde(flatten)]
159    pub extra: std::collections::HashMap<String, serde_json::Value>,
160}
161
162impl Data {
163    pub fn is_empty_extra(&self) -> bool {
164        let res = self.extra.is_empty();
165        if !res {
166            println!("Data {:?}", self.extra);
167        }
168        res
169    }
170}