Skip to main content

twapi_v2/api/
post_2_tweets.rs

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