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