Skip to main content

threads_rs/types/
post.rs

1use serde::{Deserialize, Serialize};
2
3use super::common::{
4    ChildrenData, GifAttachment, HideStatus, MediaType, PollAttachment, PollResult, PostOwner,
5    ReplyAudience, ReplyControl, TextAttachment, TextEntitiesResponse, TextEntity,
6};
7use super::ids::{ContainerId, PostId};
8use super::location::Location;
9use super::pagination::Paging;
10use super::time::ThreadsTime;
11
12/// A Threads post with all metadata and content.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Post {
15    /// Post ID.
16    pub id: PostId,
17    /// Post text content.
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub text: Option<String>,
20    /// Type of media attached.
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub media_type: Option<MediaType>,
23    /// URL of the attached media.
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub media_url: Option<String>,
26    /// Permanent link to the post.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub permalink: Option<String>,
29    /// When the post was created.
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub timestamp: Option<ThreadsTime>,
32    /// Author's username.
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub username: Option<String>,
35    /// Post owner info.
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub owner: Option<PostOwner>,
38    /// Whether this post is a reply.
39    #[serde(default)]
40    pub is_reply: bool,
41    /// Product type classification.
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub media_product_type: Option<String>,
44    /// Post shortcode.
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub shortcode: Option<String>,
47    /// Thumbnail URL for video posts.
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub thumbnail_url: Option<String>,
50    /// Alt text for media.
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub alt_text: Option<String>,
53    /// Child posts for carousels.
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub children: Option<ChildrenData>,
56    /// Whether this is a quote post.
57    #[serde(default)]
58    pub is_quote_post: bool,
59    /// Attached link URL.
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub link_attachment_url: Option<String>,
62    /// Whether the post has replies.
63    #[serde(default)]
64    pub has_replies: bool,
65    /// Reply audience setting.
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub reply_audience: Option<ReplyAudience>,
68    /// The quoted post, if any.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub quoted_post: Option<Box<Post>>,
71    /// The reposted post, if any.
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub reposted_post: Option<Box<Post>>,
74    /// GIF URL if attached.
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub gif_url: Option<String>,
77    /// Poll results, if any.
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub poll_attachment: Option<PollResult>,
80    /// Root post of the conversation.
81    #[serde(default, skip_serializing_if = "Option::is_none")]
82    pub root_post: Option<Box<Post>>,
83    /// Post being replied to.
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub replied_to: Option<Box<Post>>,
86    /// Whether the reply is owned by the authenticated user.
87    #[serde(default)]
88    pub is_reply_owned_by_me: bool,
89    /// Hide status of the post.
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub hide_status: Option<HideStatus>,
92    /// Topic tag for the post.
93    #[serde(default, skip_serializing_if = "Option::is_none")]
94    pub topic_tag: Option<String>,
95    /// Ghost post status.
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub ghost_post_status: Option<String>,
98    /// When the ghost post expires.
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    pub ghost_post_expiration_timestamp: Option<ThreadsTime>,
101    /// Whether the post author is verified on Threads.
102    #[serde(default)]
103    pub is_verified: bool,
104    /// Profile picture URL of the post author.
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub profile_picture_url: Option<String>,
107    /// Approval status of a pending reply.
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub reply_approval_status: Option<String>,
110    /// Whether the media is marked as a spoiler.
111    #[serde(default)]
112    pub is_spoiler_media: bool,
113    /// Text entities in the post content.
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub text_entities: Option<TextEntitiesResponse>,
116    /// Long-form text attachment.
117    #[serde(default, skip_serializing_if = "Option::is_none")]
118    pub text_attachment: Option<TextAttachment>,
119    /// Allowlisted country codes for geo-gating.
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub allowlisted_country_codes: Option<Vec<String>>,
122    /// Location ID tagged in the post.
123    #[serde(default, skip_serializing_if = "Option::is_none")]
124    pub location_id: Option<String>,
125    /// Location details tagged in the post.
126    #[serde(default, skip_serializing_if = "Option::is_none")]
127    pub location: Option<Location>,
128}
129
130/// Generic post content base.
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct PostContent {
133    /// Post text content.
134    #[serde(default, skip_serializing_if = "Option::is_none")]
135    pub text: Option<String>,
136    /// Media type string.
137    #[serde(default, skip_serializing_if = "Option::is_none")]
138    pub media_type: Option<String>,
139    /// ID of the post being replied to.
140    #[serde(default, skip_serializing_if = "Option::is_none")]
141    pub reply_to_id: Option<PostId>,
142}
143
144/// Content for creating a text post.
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct TextPostContent {
147    /// Post text content.
148    pub text: String,
149    /// Link attachment URL.
150    #[serde(default, skip_serializing_if = "Option::is_none")]
151    pub link_attachment: Option<String>,
152    /// Poll attachment options.
153    #[serde(default, skip_serializing_if = "Option::is_none")]
154    pub poll_attachment: Option<PollAttachment>,
155    /// Reply control setting.
156    #[serde(default, skip_serializing_if = "Option::is_none")]
157    pub reply_control: Option<ReplyControl>,
158    /// ID of the post being replied to.
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub reply_to_id: Option<PostId>,
161    /// Topic tag for the post.
162    #[serde(default, skip_serializing_if = "Option::is_none")]
163    pub topic_tag: Option<String>,
164    /// Allowlisted country codes for visibility.
165    #[serde(default, skip_serializing_if = "Option::is_none")]
166    pub allowlisted_country_codes: Option<Vec<String>>,
167    /// Location ID to tag.
168    #[serde(default, skip_serializing_if = "Option::is_none")]
169    pub location_id: Option<String>,
170    /// Whether to auto-publish the text post.
171    #[serde(default)]
172    pub auto_publish_text: bool,
173    /// ID of the post being quoted.
174    #[serde(default, skip_serializing_if = "Option::is_none")]
175    pub quoted_post_id: Option<PostId>,
176    /// Text entities for spoiler styling.
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub text_entities: Option<Vec<TextEntity>>,
179    /// Long-form text attachment.
180    #[serde(default, skip_serializing_if = "Option::is_none")]
181    pub text_attachment: Option<TextAttachment>,
182    /// GIF attachment.
183    #[serde(default, skip_serializing_if = "Option::is_none")]
184    pub gif_attachment: Option<GifAttachment>,
185    /// Whether this is a ghost post.
186    #[serde(default)]
187    pub is_ghost_post: bool,
188    /// Whether reply approvals are enabled.
189    #[serde(default)]
190    pub enable_reply_approvals: bool,
191}
192
193/// Content for creating an image post.
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct ImagePostContent {
196    /// Post text content.
197    #[serde(default, skip_serializing_if = "Option::is_none")]
198    pub text: Option<String>,
199    /// Image URL.
200    pub image_url: String,
201    /// Alt text for the image.
202    #[serde(default, skip_serializing_if = "Option::is_none")]
203    pub alt_text: Option<String>,
204    /// Reply control setting.
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub reply_control: Option<ReplyControl>,
207    /// ID of the post being replied to.
208    #[serde(default, skip_serializing_if = "Option::is_none")]
209    pub reply_to_id: Option<PostId>,
210    /// Topic tag for the post.
211    #[serde(default, skip_serializing_if = "Option::is_none")]
212    pub topic_tag: Option<String>,
213    /// Allowlisted country codes for visibility.
214    #[serde(default, skip_serializing_if = "Option::is_none")]
215    pub allowlisted_country_codes: Option<Vec<String>>,
216    /// Location ID to tag.
217    #[serde(default, skip_serializing_if = "Option::is_none")]
218    pub location_id: Option<String>,
219    /// ID of the post being quoted.
220    #[serde(default, skip_serializing_if = "Option::is_none")]
221    pub quoted_post_id: Option<PostId>,
222    /// Text entities for spoiler styling.
223    #[serde(default, skip_serializing_if = "Option::is_none")]
224    pub text_entities: Option<Vec<TextEntity>>,
225    /// Whether the media is marked as a spoiler.
226    #[serde(default)]
227    pub is_spoiler_media: bool,
228    /// Whether reply approvals are enabled.
229    #[serde(default)]
230    pub enable_reply_approvals: bool,
231}
232
233/// Content for creating a video post.
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct VideoPostContent {
236    /// Post text content.
237    #[serde(default, skip_serializing_if = "Option::is_none")]
238    pub text: Option<String>,
239    /// Video URL.
240    pub video_url: String,
241    /// Alt text for the video.
242    #[serde(default, skip_serializing_if = "Option::is_none")]
243    pub alt_text: Option<String>,
244    /// Reply control setting.
245    #[serde(default, skip_serializing_if = "Option::is_none")]
246    pub reply_control: Option<ReplyControl>,
247    /// ID of the post being replied to.
248    #[serde(default, skip_serializing_if = "Option::is_none")]
249    pub reply_to_id: Option<PostId>,
250    /// Topic tag for the post.
251    #[serde(default, skip_serializing_if = "Option::is_none")]
252    pub topic_tag: Option<String>,
253    /// Allowlisted country codes for visibility.
254    #[serde(default, skip_serializing_if = "Option::is_none")]
255    pub allowlisted_country_codes: Option<Vec<String>>,
256    /// Location ID to tag.
257    #[serde(default, skip_serializing_if = "Option::is_none")]
258    pub location_id: Option<String>,
259    /// ID of the post being quoted.
260    #[serde(default, skip_serializing_if = "Option::is_none")]
261    pub quoted_post_id: Option<PostId>,
262    /// Text entities for spoiler styling.
263    #[serde(default, skip_serializing_if = "Option::is_none")]
264    pub text_entities: Option<Vec<TextEntity>>,
265    /// Whether the media is marked as a spoiler.
266    #[serde(default)]
267    pub is_spoiler_media: bool,
268    /// Whether reply approvals are enabled.
269    #[serde(default)]
270    pub enable_reply_approvals: bool,
271}
272
273/// Content for creating a carousel post.
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct CarouselPostContent {
276    /// Post text content.
277    #[serde(default, skip_serializing_if = "Option::is_none")]
278    pub text: Option<String>,
279    /// Container IDs for the carousel items.
280    pub children: Vec<ContainerId>,
281    /// Reply control setting.
282    #[serde(default, skip_serializing_if = "Option::is_none")]
283    pub reply_control: Option<ReplyControl>,
284    /// ID of the post being replied to.
285    #[serde(default, skip_serializing_if = "Option::is_none")]
286    pub reply_to_id: Option<PostId>,
287    /// Topic tag for the post.
288    #[serde(default, skip_serializing_if = "Option::is_none")]
289    pub topic_tag: Option<String>,
290    /// Allowlisted country codes for visibility.
291    #[serde(default, skip_serializing_if = "Option::is_none")]
292    pub allowlisted_country_codes: Option<Vec<String>>,
293    /// Location ID to tag.
294    #[serde(default, skip_serializing_if = "Option::is_none")]
295    pub location_id: Option<String>,
296    /// ID of the post being quoted.
297    #[serde(default, skip_serializing_if = "Option::is_none")]
298    pub quoted_post_id: Option<PostId>,
299    /// Text entities for spoiler styling.
300    #[serde(default, skip_serializing_if = "Option::is_none")]
301    pub text_entities: Option<Vec<TextEntity>>,
302    /// Whether the media is marked as a spoiler.
303    #[serde(default)]
304    pub is_spoiler_media: bool,
305    /// Whether reply approvals are enabled.
306    #[serde(default)]
307    pub enable_reply_approvals: bool,
308}
309
310/// Paginated response containing multiple posts.
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct PostsResponse {
313    /// List of posts.
314    pub data: Vec<Post>,
315    /// Pagination info.
316    #[serde(default)]
317    pub paging: Paging,
318}
319
320/// Paginated response containing reply posts.
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct RepliesResponse {
323    /// List of reply posts.
324    pub data: Vec<Post>,
325    /// Pagination info.
326    #[serde(default)]
327    pub paging: Paging,
328}
329
330/// Response containing insights data.
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct InsightsResponse {
333    /// List of insight metrics.
334    pub data: Vec<super::insights::Insight>,
335}