mirror of
https://framagit.org/veretcle/oolatoocs.git
synced 2025-07-20 12:31:18 +02:00
feat: threads
This commit is contained in:
13
src/lib.rs
13
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
|
// 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 {
|
for media in toot.media_attachments {
|
||||||
let id = match media.r#type {
|
let id = match media.r#type {
|
||||||
AttachmentType::Image => {
|
AttachmentType::Image => {
|
||||||
@@ -77,12 +78,20 @@ pub async fn run(config: &Config) {
|
|||||||
medias.push(id);
|
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::<u64>().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
|
.await
|
||||||
.unwrap_or_else(|e| panic!("Cannot Tweet {}: {}", toot.id, e));
|
.unwrap_or_else(|e| panic!("Cannot Tweet {}: {}", toot.id, e));
|
||||||
|
|
||||||
|
// writes the current state of the tweet
|
||||||
write_state(
|
write_state(
|
||||||
&conn,
|
&conn,
|
||||||
TweetToToot {
|
TweetToToot {
|
||||||
|
@@ -7,7 +7,7 @@ use reqwest::{
|
|||||||
Body, Client,
|
Body, Client,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::error::Error;
|
use std::{error::Error, ops::Not};
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
const TWITTER_API_TWEET_URL: &str = "https://api.twitter.com/2/tweets";
|
const TWITTER_API_TWEET_URL: &str = "https://api.twitter.com/2/tweets";
|
||||||
@@ -20,24 +20,32 @@ const TWITTER_METADATA_MEDIA_URL: &str =
|
|||||||
struct EmptyRequest {}
|
struct EmptyRequest {}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct Tweet {
|
struct Tweet {
|
||||||
pub text: String,
|
text: String,
|
||||||
pub media: TweetMediasIds,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
media: Option<TweetMediasIds>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
reply: Option<TweetReply>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct TweetMediasIds {
|
struct TweetMediasIds {
|
||||||
pub media_ids: Vec<String>,
|
media_ids: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct TweetReply {
|
||||||
|
in_reply_to_tweet_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct TweetResponse {
|
struct TweetResponse {
|
||||||
pub data: TweetResponseData,
|
data: TweetResponseData,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct TweetResponseData {
|
struct TweetResponseData {
|
||||||
pub id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@@ -112,7 +120,7 @@ pub async fn upload_simple_media(
|
|||||||
|
|
||||||
// upload the media
|
// upload the media
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let res: UploadMediaResponse = client
|
let res = client
|
||||||
.post(TWITTER_UPLOAD_MEDIA_URL)
|
.post(TWITTER_UPLOAD_MEDIA_URL)
|
||||||
.header(
|
.header(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
@@ -129,7 +137,7 @@ pub async fn upload_simple_media(
|
|||||||
))
|
))
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json()
|
.json::<UploadMediaResponse>()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
debug!("Media ID: {}", res.media_id);
|
debug!("Media ID: {}", res.media_id);
|
||||||
@@ -201,7 +209,7 @@ pub async fn upload_chunk_media(
|
|||||||
debug!("Init the slot for uploading media: {}", u);
|
debug!("Init the slot for uploading media: {}", u);
|
||||||
// init the slot for uploading
|
// init the slot for uploading
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let orig_media_id: UploadMediaResponse = client
|
let orig_media_id = client
|
||||||
.post(TWITTER_UPLOAD_MEDIA_URL)
|
.post(TWITTER_UPLOAD_MEDIA_URL)
|
||||||
.header(
|
.header(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
@@ -221,7 +229,7 @@ pub async fn upload_chunk_media(
|
|||||||
)
|
)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json()
|
.json::<UploadMediaResponse>()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
debug!("Slot initiated with ID: {}", orig_media_id.media_id);
|
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);
|
debug!("Finalize media ID: {}", orig_media_id.media_id);
|
||||||
// Finalizing task
|
// Finalizing task
|
||||||
let fin: UploadMediaResponse = client
|
let fin = client
|
||||||
.post(TWITTER_UPLOAD_MEDIA_URL)
|
.post(TWITTER_UPLOAD_MEDIA_URL)
|
||||||
.header(
|
.header(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
@@ -285,7 +293,7 @@ pub async fn upload_chunk_media(
|
|||||||
)
|
)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json()
|
.json::<UploadMediaResponse>()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(p_info) = fin.processing_info {
|
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
|
orig_media_id.media_id, wait_sec
|
||||||
);
|
);
|
||||||
|
|
||||||
let status: UploadMediaResponse = client
|
let status = client
|
||||||
.get(TWITTER_UPLOAD_MEDIA_URL)
|
.get(TWITTER_UPLOAD_MEDIA_URL)
|
||||||
.header(
|
.header(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
@@ -324,7 +332,7 @@ pub async fn upload_chunk_media(
|
|||||||
.query(&command)
|
.query(&command)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json()
|
.json::<UploadMediaResponse>()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let p_status = status.processing_info.unwrap(); // shouldn’t be None at this point
|
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…",
|
"Processing still pending, waiting {} secs more…",
|
||||||
p_status.check_after_secs.unwrap() // unwrap is safe here,
|
p_status.check_after_secs.unwrap() // unwrap is safe here,
|
||||||
// check_after_secs is only present
|
// 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;
|
sleep(Duration::from_secs(p_status.check_after_secs.unwrap())).await;
|
||||||
continue;
|
continue;
|
||||||
@@ -365,15 +374,19 @@ pub async fn post_tweet(
|
|||||||
config: &TwitterConfig,
|
config: &TwitterConfig,
|
||||||
content: &str,
|
content: &str,
|
||||||
medias: &[u64],
|
medias: &[u64],
|
||||||
|
reply_to: &Option<u64>,
|
||||||
) -> Result<u64, Box<dyn Error>> {
|
) -> Result<u64, Box<dyn Error>> {
|
||||||
let empty_request = EmptyRequest {}; // Why? Because fuck you, that’s why!
|
let empty_request = EmptyRequest {}; // Why? Because fuck you, that’s why!
|
||||||
let token = get_token(config);
|
let token = get_token(config);
|
||||||
|
|
||||||
let tweet = Tweet {
|
let tweet = Tweet {
|
||||||
text: content.to_string(),
|
text: content.to_string(),
|
||||||
media: TweetMediasIds {
|
media: medias.is_empty().not().then(|| TweetMediasIds {
|
||||||
media_ids: medias.iter().map(|m| m.to_string()).collect(),
|
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();
|
let client = Client::new();
|
||||||
|
Reference in New Issue
Block a user