diff --git a/src/lib.rs b/src/lib.rs index 54de55a..5829080 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ pub async fn run(config: &Config) { // if we wanted to cut toot in half, now would be the right time to do so + // treats media for media in toot.media_attachments { let id = match media.r#type { AttachmentType::Image => { @@ -77,12 +78,20 @@ pub async fn run(config: &Config) { medias.push(id); } - println!("{:?}", medias); + // threads if necessary + let reply_to = toot.in_reply_to_id.and_then(|t| { + read_state(&conn, Some(t.parse::().unwrap())) + .ok() + .flatten() + .map(|s| s.tweet_id) + }); - let tweet_id = post_tweet(&config.twitter, &tweet_content, &medias) + // posts corresponding tweet + let tweet_id = post_tweet(&config.twitter, &tweet_content, &medias, &reply_to) .await .unwrap_or_else(|e| panic!("Cannot Tweet {}: {}", toot.id, e)); + // writes the current state of the tweet write_state( &conn, TweetToToot { diff --git a/src/twitter.rs b/src/twitter.rs index 0e60136..8078c90 100644 --- a/src/twitter.rs +++ b/src/twitter.rs @@ -7,7 +7,7 @@ use reqwest::{ Body, Client, }; use serde::{Deserialize, Serialize}; -use std::error::Error; +use std::{error::Error, ops::Not}; use tokio::time::{sleep, Duration}; const TWITTER_API_TWEET_URL: &str = "https://api.twitter.com/2/tweets"; @@ -20,24 +20,32 @@ const TWITTER_METADATA_MEDIA_URL: &str = struct EmptyRequest {} #[derive(Serialize, Debug)] -pub struct Tweet { - pub text: String, - pub media: TweetMediasIds, +struct Tweet { + text: String, + #[serde(skip_serializing_if = "Option::is_none")] + media: Option, + #[serde(skip_serializing_if = "Option::is_none")] + reply: Option, } #[derive(Serialize, Debug)] -pub struct TweetMediasIds { - pub media_ids: Vec, +struct TweetMediasIds { + media_ids: Vec, +} + +#[derive(Serialize, Debug)] +struct TweetReply { + in_reply_to_tweet_id: String, } #[derive(Deserialize, Debug)] -pub struct TweetResponse { - pub data: TweetResponseData, +struct TweetResponse { + data: TweetResponseData, } #[derive(Deserialize, Debug)] -pub struct TweetResponseData { - pub id: String, +struct TweetResponseData { + id: String, } #[derive(Deserialize, Debug)] @@ -112,7 +120,7 @@ pub async fn upload_simple_media( // upload the media let client = Client::new(); - let res: UploadMediaResponse = client + let res = client .post(TWITTER_UPLOAD_MEDIA_URL) .header( "Authorization", @@ -129,7 +137,7 @@ pub async fn upload_simple_media( )) .send() .await? - .json() + .json::() .await?; debug!("Media ID: {}", res.media_id); @@ -201,7 +209,7 @@ pub async fn upload_chunk_media( debug!("Init the slot for uploading media: {}", u); // init the slot for uploading let client = Client::new(); - let orig_media_id: UploadMediaResponse = client + let orig_media_id = client .post(TWITTER_UPLOAD_MEDIA_URL) .header( "Authorization", @@ -221,7 +229,7 @@ pub async fn upload_chunk_media( ) .send() .await? - .json() + .json::() .await?; debug!("Slot initiated with ID: {}", orig_media_id.media_id); @@ -267,7 +275,7 @@ pub async fn upload_chunk_media( debug!("Finalize media ID: {}", orig_media_id.media_id); // Finalizing task - let fin: UploadMediaResponse = client + let fin = client .post(TWITTER_UPLOAD_MEDIA_URL) .header( "Authorization", @@ -285,7 +293,7 @@ pub async fn upload_chunk_media( ) .send() .await? - .json() + .json::() .await?; if let Some(p_info) = fin.processing_info { @@ -310,7 +318,7 @@ pub async fn upload_chunk_media( orig_media_id.media_id, wait_sec ); - let status: UploadMediaResponse = client + let status = client .get(TWITTER_UPLOAD_MEDIA_URL) .header( "Authorization", @@ -324,7 +332,7 @@ pub async fn upload_chunk_media( .query(&command) .send() .await? - .json() + .json::() .await?; let p_status = status.processing_info.unwrap(); // shouldn’t be None at this point @@ -347,7 +355,8 @@ pub async fn upload_chunk_media( "Processing still pending, waiting {} secs more…", p_status.check_after_secs.unwrap() // unwrap is safe here, // check_after_secs is only present - // when status is pending + // when status is pending or in + // progress ); sleep(Duration::from_secs(p_status.check_after_secs.unwrap())).await; continue; @@ -365,15 +374,19 @@ pub async fn post_tweet( config: &TwitterConfig, content: &str, medias: &[u64], + reply_to: &Option, ) -> Result> { let empty_request = EmptyRequest {}; // Why? Because fuck you, that’s why! let token = get_token(config); let tweet = Tweet { text: content.to_string(), - media: TweetMediasIds { + media: medias.is_empty().not().then(|| TweetMediasIds { media_ids: medias.iter().map(|m| m.to_string()).collect(), - }, + }), + reply: reply_to.map(|s| TweetReply { + in_reply_to_tweet_id: s.to_string(), + }), }; let client = Client::new();