mirror of
https://framagit.org/veretcle/oolatoocs.git
synced 2025-07-20 12:31:18 +02:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8f23c2459b | ||
![]() |
26805feadb | ||
![]() |
3a8fd538fc | ||
![]() |
891f46ec2f |
87
Cargo.lock
generated
87
Cargo.lock
generated
@@ -479,6 +479,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
@@ -733,6 +743,21 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
@@ -740,6 +765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -748,6 +774,17 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
@@ -783,6 +820,7 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
@@ -1522,9 +1560,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "megalodon"
|
||||
version = "0.13.10"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef39d0ff14ab57bc76f675ca00dae297eb2e75ae28281a0b7a451bcf9cc94b6"
|
||||
checksum = "6053001f2fd3082f7c6007225fef9cf6c0bbea96d8fc82976fb0dcfda3bcbf30"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -1630,7 +1668,7 @@ dependencies = [
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework 2.11.1",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
@@ -1781,13 +1819,14 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "oolatoocs"
|
||||
version = "4.1.1"
|
||||
version = "4.1.3"
|
||||
dependencies = [
|
||||
"atrium-api",
|
||||
"bsky-sdk",
|
||||
"chrono",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"html-escape",
|
||||
"image",
|
||||
"log",
|
||||
@@ -1830,9 +1869,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
@@ -2396,15 +2435,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.7.3"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
|
||||
checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2499,7 +2537,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
@@ -2745,7 +2796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
@@ -2938,9 +2989,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.23.1"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd"
|
||||
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
@@ -3076,9 +3127,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.23.0"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
|
||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -3109,9 +3160,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "oolatoocs"
|
||||
version = "4.1.1"
|
||||
version = "4.1.3"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -9,9 +9,10 @@ edition = "2021"
|
||||
chrono = "^0.4"
|
||||
clap = "^4"
|
||||
env_logger = "^0.10"
|
||||
futures = "^0.3"
|
||||
html-escape = "^0.2"
|
||||
log = "^0.4"
|
||||
megalodon = "^0.13"
|
||||
megalodon = "^1.0"
|
||||
oauth1-request = "^0.6"
|
||||
regex = "^1.10"
|
||||
reqwest = { version = "^0.11", features = ["json", "stream", "multipart"] }
|
||||
|
91
src/bsky.rs
91
src/bsky.rs
@@ -8,8 +8,9 @@ use bsky_sdk::{
|
||||
rich_text::RichText,
|
||||
BskyAgent,
|
||||
};
|
||||
use futures::{stream, StreamExt};
|
||||
use image::ImageReader;
|
||||
use log::{debug, error};
|
||||
use log::{debug, error, warn};
|
||||
use megalodon::entities::attachment::{Attachment, AttachmentType};
|
||||
use regex::Regex;
|
||||
use std::{error::Error, fs::exists, io::Cursor};
|
||||
@@ -152,39 +153,66 @@ pub async fn generate_media_records(
|
||||
let mut embed: Option<
|
||||
atrium_api::types::Union<atrium_api::app::bsky::feed::post::RecordEmbedRefs>,
|
||||
> = None;
|
||||
let mut images = Vec::new();
|
||||
let mut videos: Vec<atrium_api::app::bsky::embed::video::MainData> = Vec::new();
|
||||
|
||||
for media in media_attach.iter() {
|
||||
match media.r#type {
|
||||
AttachmentType::Image => {
|
||||
let blob = upload_media(true, bsky, &media.url).await.unwrap();
|
||||
let image_media_attach: Vec<_> = media_attach
|
||||
.iter()
|
||||
.filter(|x| x.r#type == AttachmentType::Image)
|
||||
.cloned()
|
||||
.collect();
|
||||
let video_media_attach: Vec<_> = media_attach
|
||||
.iter()
|
||||
.filter(|x| (x.r#type == AttachmentType::Video || x.r#type == AttachmentType::Gifv))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
images.push(
|
||||
// Bsky only tasks 1 video per post, so we’ll try to treat that first and exit
|
||||
if !video_media_attach.is_empty() {
|
||||
// treat only the very first video, ignore the rest
|
||||
let media = &video_media_attach[0];
|
||||
let blob = upload_media(false, bsky, &media.url).await.unwrap();
|
||||
|
||||
embed = Some(atrium_api::types::Union::Refs(
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(Box::new(
|
||||
atrium_api::app::bsky::embed::video::MainData {
|
||||
alt: media.description.clone(),
|
||||
aspect_ratio: None,
|
||||
captions: None,
|
||||
video: blob.data.blob,
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
));
|
||||
|
||||
// returns immediately, we don’t want to treat the other medias
|
||||
return embed;
|
||||
}
|
||||
|
||||
let mut stream = stream::iter(image_media_attach)
|
||||
.map(|media| {
|
||||
let bsky = bsky.clone();
|
||||
tokio::task::spawn(async move {
|
||||
debug!("Treating media {}", &media.url);
|
||||
upload_media(true, &bsky, &media.url).await.map(|i| {
|
||||
atrium_api::app::bsky::embed::images::ImageData {
|
||||
alt: media
|
||||
.description
|
||||
.clone()
|
||||
.map_or("".to_string(), |v| v.to_owned()),
|
||||
aspect_ratio: None,
|
||||
image: blob.data.blob,
|
||||
image: i.data.blob,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
AttachmentType::Gifv | AttachmentType::Video => {
|
||||
let blob = upload_media(false, bsky, &media.url).await.unwrap();
|
||||
})
|
||||
})
|
||||
})
|
||||
.buffered(4);
|
||||
|
||||
videos.push(atrium_api::app::bsky::embed::video::MainData {
|
||||
alt: media.description.clone(),
|
||||
aspect_ratio: None,
|
||||
captions: None,
|
||||
video: blob.data.blob,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
error!("Not an image, not a video, what happened here?");
|
||||
}
|
||||
let mut images = Vec::new();
|
||||
|
||||
while let Some(result) = stream.next().await {
|
||||
match result {
|
||||
Ok(Ok(v)) => images.push(v.into()),
|
||||
Ok(Err(e)) => warn!("Cannot treat a specific media: {}", e),
|
||||
Err(e) => error!("Something went wrong when joining main thread: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,19 +224,14 @@ pub async fn generate_media_records(
|
||||
));
|
||||
}
|
||||
|
||||
// if a video has been uploaded, it takes priority as you can only have 1 video per post
|
||||
if !videos.is_empty() {
|
||||
embed = Some(atrium_api::types::Union::Refs(
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(Box::new(
|
||||
videos[0].clone().into(),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
embed
|
||||
}
|
||||
|
||||
async fn upload_media(is_image: bool, bsky: &BskyAgent, u: &str) -> Result<Output, Box<dyn Error>> {
|
||||
async fn upload_media(
|
||||
is_image: bool,
|
||||
bsky: &BskyAgent,
|
||||
u: &str,
|
||||
) -> Result<Output, Box<dyn Error + Send + Sync>> {
|
||||
let dl = reqwest::get(u).await?;
|
||||
let content_length = dl.content_length().ok_or("Content length unavailable")?;
|
||||
let bytes = if content_length <= 1_000_000 || !is_image {
|
||||
|
@@ -27,7 +27,8 @@ pub async fn run(config: &Config) {
|
||||
let conn = Connection::open(&config.oolatoocs.db_path)
|
||||
.unwrap_or_else(|e| panic!("Cannot open DB: {}", e));
|
||||
|
||||
let mastodon = get_mastodon_instance(&config.mastodon);
|
||||
let mastodon = get_mastodon_instance(&config.mastodon)
|
||||
.unwrap_or_else(|e| panic!("Cannot instantiate Mastodon: {}", e));
|
||||
|
||||
let bluesky = get_session(&config.bluesky)
|
||||
.await
|
||||
|
@@ -12,12 +12,12 @@ use std::error::Error;
|
||||
use std::io::stdin;
|
||||
|
||||
/// Get Mastodon Object instance
|
||||
pub fn get_mastodon_instance(config: &MastodonConfig) -> Mastodon {
|
||||
Mastodon::new(
|
||||
pub fn get_mastodon_instance(config: &MastodonConfig) -> Result<Mastodon, Box<dyn Error>> {
|
||||
Ok(Mastodon::new(
|
||||
config.base.to_string(),
|
||||
Some(config.token.to_string()),
|
||||
None,
|
||||
)
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Get the edited_at field from the specified toot
|
||||
@@ -71,7 +71,8 @@ pub async fn get_mastodon_timeline_since(
|
||||
/// Most of this function is a direct copy/paste of the official `elefren` crate
|
||||
#[tokio::main]
|
||||
pub async fn register(host: &str) {
|
||||
let mastodon = generator(megalodon::SNS::Mastodon, host.to_string(), None, None);
|
||||
let mastodon = generator(megalodon::SNS::Mastodon, host.to_string(), None, None)
|
||||
.expect("Cannot build Mastodon generator object");
|
||||
|
||||
let options = AppInputOptions {
|
||||
redirect_uris: None,
|
||||
|
Reference in New Issue
Block a user