1use crate::responses::errors::Errors;
2use crate::{
3 api::{Authentication, TwapiOptions, apply_options, 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(
106 apply_options(builder, &self.twapi_options),
107 "POST",
108 &url,
109 &[],
110 )
111 }
112
113 pub async fn execute(
114 self,
115 authentication: &impl Authentication,
116 ) -> Result<(Response, Headers), Error> {
117 execute_twitter(self.build(authentication)).await
118 }
119}
120
121#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
122pub struct Response {
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub data: Option<Data>,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub errors: Option<Vec<Errors>>,
127 #[serde(flatten)]
128 pub extra: std::collections::HashMap<String, serde_json::Value>,
129}
130
131impl Response {
132 pub fn is_empty_extra(&self) -> bool {
133 let res = self.extra.is_empty()
134 && self
135 .data
136 .as_ref()
137 .map(|it| it.is_empty_extra())
138 .unwrap_or(true)
139 && self
140 .errors
141 .as_ref()
142 .map(|it| it.iter().all(|item| item.is_empty_extra()))
143 .unwrap_or(true);
144 if !res {
145 println!("Response {:?}", self.extra);
146 }
147 res
148 }
149}
150
151#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
152pub struct Data {
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub id: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub text: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub edit_history_tweet_ids: Option<Vec<String>>,
159 #[serde(flatten)]
160 pub extra: std::collections::HashMap<String, serde_json::Value>,
161}
162
163impl Data {
164 pub fn is_empty_extra(&self) -> bool {
165 let res = self.extra.is_empty();
166 if !res {
167 println!("Data {:?}", self.extra);
168 }
169 res
170 }
171}