diff --git a/Cargo.lock b/Cargo.lock index 87efdd5..82b6803 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,9 +494,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1645,9 +1645,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1806,7 +1806,7 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oolatoocs" -version = "4.1.4" +version = "4.2.0" dependencies = [ "atrium-api", "bsky-sdk", @@ -1830,9 +1830,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ "bitflags 2.8.0", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 1ae9318..b60b05c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oolatoocs" -version = "4.1.4" +version = "4.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -21,7 +21,7 @@ serde = { version = "^1.0", features = ["derive"] } tokio = { version = "^1.33", features = ["rt-multi-thread", "macros"] } toml = "^0.8" bsky-sdk = "^0.1" -atrium-api = "^0.24" +atrium-api = { version = "^0.24", features = ["namespace-appbsky"] } image = "^0.25" webp = "^0.3" diff --git a/src/bsky.rs b/src/bsky.rs index b711cb5..a35a216 100644 --- a/src/bsky.rs +++ b/src/bsky.rs @@ -11,7 +11,10 @@ use bsky_sdk::{ use futures::{stream, StreamExt}; use image::ImageReader; use log::{debug, error, warn}; -use megalodon::entities::attachment::{Attachment, AttachmentType}; +use megalodon::entities::{ + attachment::{Attachment, AttachmentType}, + card::Card, +}; use regex::Regex; use std::{error::Error, fs::exists, io::Cursor}; use webp::*; @@ -145,7 +148,43 @@ async fn get_record( Ok(record) } -// it’s ugly af but it gets the job done for now +/// Generate an embed card record into Bsky +/// If the preview image does not exist or fails to upload, it is simply ignored +pub async fn generate_embed_records( + bsky: &BskyAgent, + card: &Card, +) -> Option> { + // uploads the image card, if it fails, simply ignore everything + let blob = if let Some(url) = &card.image { + if let Ok(image_blob) = upload_media(true, bsky, url).await { + Some(image_blob.blob.clone()) + } else { + None + } + } else { + None + }; + + let record_card = atrium_api::app::bsky::embed::external::ExternalData { + description: card.description.clone(), + thumb: blob, + title: card.title.clone(), + uri: card.url.clone(), + }; + + Some(atrium_api::types::Union::Refs( + atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedExternalMain(Box::new( + atrium_api::app::bsky::embed::external::MainData { + external: record_card.into(), + } + .into(), + )), + )) +} + +/// Generate an array of Bsky media records +/// As Bsky does not support multiple video in a record or mix of video and images, video has the +/// highest priority pub async fn generate_media_records( bsky: &BskyAgent, media_attach: &[Attachment], diff --git a/src/lib.rs b/src/lib.rs index 7e83534..2f77a4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,9 @@ mod utils; use utils::{generate_multi_tweets, strip_everything}; mod bsky; -use bsky::{build_post_record, generate_media_records, get_session, BskyReply}; +use bsky::{ + build_post_record, generate_embed_records, generate_media_records, get_session, BskyReply, +}; use rusqlite::Connection; @@ -152,14 +154,21 @@ pub async fn run(config: &Config) { }; // treats medias - let record_medias = generate_media_records(&bluesky, &toot.media_attachments).await; + let mut record_embed = generate_media_records(&bluesky, &toot.media_attachments).await; + + // treats embed cards if any + if let Some(card) = &toot.card { + if record_embed.is_none() { + record_embed = generate_embed_records(&bluesky, card).await; + } + } // posts corresponding tweet let record = build_post_record( &config.bluesky, &tweet_content, &toot.language, - record_medias, + record_embed, &record_reply_to, ) .await