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}