From 97cdfbb7ef40a477d4afa4c3d8fa4f1fd7616011 Mon Sep 17 00:00:00 2001 From: Denis-Cosmin NUTIU Date: Tue, 31 Dec 2024 12:11:46 +0200 Subject: [PATCH] implement posts with images --- bot/src/bluesky.rs | 21 ++++++++++++++++----- bot/src/bluesky/atproto.rs | 15 +++++++++++++++ bot/src/main.rs | 20 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/bot/src/bluesky.rs b/bot/src/bluesky.rs index 5b39db5..125957b 100644 --- a/bot/src/bluesky.rs +++ b/bot/src/bluesky.rs @@ -3,9 +3,9 @@ mod token; use crate::bluesky::atproto::{ATProtoServerCreateSession, BlobResponse}; use anyhow::anyhow; -use base64::prelude::BASE64_STANDARD; use base64::Engine; use reqwest::Body; +use std::io::Write; use token::Token; /// The BlueSky client used to interact with the platform. @@ -85,17 +85,28 @@ impl BlueSkyClient { } /// Uploads an image. - async fn upload_image(&mut self, payload: Vec) -> Result { - let base64_data = BASE64_STANDARD.encode(payload); + pub async fn upload_image_by_url( + &mut self, + image_url: &str, + ) -> Result { + let data: Vec = self + .client + .get(image_url) + .send() + .await? + .bytes() + .await? + .to_vec(); + Ok(self .client .post("https://bsky.social/xrpc/com.atproto.repo.uploadBlob") - .header("Content-Type", "image/png") + .header("Content-Type", "image/jpeg") .header( "Authorization", format!("Bearer {}", self.auth_token.access_jwt), ) - .body(base64_data) + .body(data) .send() .await? .json() diff --git a/bot/src/bluesky/atproto.rs b/bot/src/bluesky/atproto.rs index acc3876..c8340f9 100644 --- a/bot/src/bluesky/atproto.rs +++ b/bot/src/bluesky/atproto.rs @@ -20,6 +20,7 @@ pub struct Blob { } impl Blob { + #![allow(dead_code)] pub fn new(link: &str, mime_type: &str, size: i64) -> Self { Blob { _type: "blob".to_string(), @@ -235,4 +236,18 @@ mod tests { Ok(()) } + + #[test] + fn test_blob_new_serialization() -> Result<(), anyhow::Error> { + let blob = Blob::new("asa", "image/jpeg", 1); + + let json = serde_json::to_string(&blob)?; + + assert_eq!( + json, + r#"{"$type":"blob","ref":{"$link":"asa"},"mimeType":"image/jpeg","size":1}"# + ); + + Ok(()) + } } diff --git a/bot/src/main.rs b/bot/src/main.rs index 7866dab..75b24ed 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -32,6 +32,18 @@ fn setup_graceful_shutdown(running: &Arc) { }); } +/// Embeds an image to a post. +async fn add_image_to_post( + client: &mut BlueSkyClient, + image_url: &str, + record: &mut bluesky::atproto::ATProtoRepoCreateRecord, +) -> Result<(), anyhow::Error> { + let thumb = client.upload_image_by_url(image_url).await?; + record.record.embed.as_mut().unwrap().external.thumb = Some(thumb.blob); + + Ok(()) +} + #[tokio::main] async fn main() -> Result<(), anyhow::Error> { env_logger::init(); @@ -70,6 +82,14 @@ async fn main() -> Result<(), anyhow::Error> { Ok(post) => { let mut data: bluesky::atproto::ATProtoRepoCreateRecord = post.clone().into(); data.repo = args.bluesky_handle.clone(); + + if let Some(image_link) = post.image.clone() { + let result = + add_image_to_post(&mut bluesky_client, &image_link, &mut data).await; + if let Err(err) = result { + warn!("Failed to upload image: {err}") + } + } let json = serde_json::to_string(&data); match json { Ok(json) => {