diff --git a/Cargo.lock b/Cargo.lock index 65f5928..fc15ecc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atrium-api" version = "0.24.7" @@ -255,6 +261,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -418,6 +430,7 @@ dependencies = [ "dotenvy", "ipld-core", "lazy_static", + "reqwest", "rocket", "serde", "serde_json", @@ -630,6 +643,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.0" @@ -726,7 +758,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -749,6 +781,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -759,6 +792,24 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.5.0", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -1267,6 +1318,55 @@ version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -1388,12 +1488,15 @@ dependencies = [ "async-compression", "base64", "bytes", + "encoding_rs", "futures-core", "futures-util", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", "hyper 1.5.0", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -1404,22 +1507,43 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "windows-registry", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rocket" version = "0.5.1" @@ -1507,6 +1631,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustix" version = "0.38.38" @@ -1520,6 +1650,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -1535,6 +1679,17 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.18" @@ -1739,6 +1894,12 @@ dependencies = [ "loom", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1770,6 +1931,27 @@ dependencies = [ "futures-core", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.13.0" @@ -1897,6 +2079,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.16" @@ -2097,6 +2290,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.2" @@ -2218,6 +2417,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2465,3 +2673,9 @@ dependencies = [ "quote", "syn 2.0.86", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 5fa5baf..81ba98e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ thiserror = "1.0.66" serde = "1.0.213" serde_json = "1.0.132" url = "2.5.2" +reqwest = { version = "*", features = ["rustls-tls", "json"] } diff --git a/src/bsky.rs b/src/bsky.rs index a808403..2011cae 100644 --- a/src/bsky.rs +++ b/src/bsky.rs @@ -1,6 +1,6 @@ use crate::meow::{ - BotCheck, EmbedAuthor, EmbedMedia, EmbedResponse, EmbedSource, EmbedThingy, GenericError, - MissingElementError, + BotCheck, DidPlcResponse, EmbedAuthor, EmbedMedia, EmbedResponse, EmbedSource, EmbedThingy, + GenericError, MissingElementError, }; use crate::ManagedBskyAgent; use bsky_sdk::api::app::bsky::embed::images::ImageData; @@ -97,6 +97,13 @@ pub(crate) async fn profile_post( if let Some(embeds) = record.get_key_value("embed") { let embeds = embeds.1; + // for bsky.social we're gonna take a shortcut, as we're pretty much 100% sure that the PDS will be at bsky.social + let pds_endpoint = if name.ends_with(".bsky.social") { + "https://bsky.social".to_string() + } else { + resolve_plc_did(did).await? + }; + // multiple? images let images = match embeds.get("images") { Ok(Some(Ipld::List(images))) => { @@ -135,7 +142,7 @@ pub(crate) async fn profile_post( }; for blob in blobs { - let url = format!("https://bsky.social/xrpc/com.atproto.sync.getBlob?did={}&cid={}", did, blob.r#ref.0); + let url = format!("{}/xrpc/com.atproto.sync.getBlob?did={}&cid={}", pds_endpoint, did, blob.r#ref.0); if blob.mime_type.starts_with("image/") { media_embeds.push(EmbedMedia::Image(url)); } else { @@ -172,3 +179,20 @@ fn ipld_to_string(ipld: &Ipld) -> Result { Err(()) } } + +async fn resolve_plc_did(did: &str) -> Result { + let meow = reqwest::Client::new() + .get(format!("https://plc.directory/{}", did)) + .send() + .await? + .json::() + .await?; + + let pds_service = meow + .service + .iter() + .find(|service| service.id == "#atproto_pds") + .ok_or(GenericError::TooTired)?; + + Ok(pds_service.service_endpoint.clone()) +} diff --git a/src/meow.rs b/src/meow.rs index 512c1c3..56d2854 100644 --- a/src/meow.rs +++ b/src/meow.rs @@ -5,7 +5,7 @@ use rocket::request::{FromRequest, Outcome}; use rocket::response::{Redirect, Responder}; use rocket::{response, Request, Response}; use serde::ser::SerializeMap; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use thiserror::Error; use url::Url; @@ -217,6 +217,9 @@ pub enum GenericError { #[error("bsky sdk error")] Bsky(#[from] bsky_sdk::Error), + + #[error("reqwest error")] + Reqwest(#[from] reqwest::Error), } impl Serialize for GenericError { @@ -231,6 +234,7 @@ impl Serialize for GenericError { GenericError::GetBlob(e) => e.to_string(), GenericError::InvalidCID(e) => e.to_string(), GenericError::Bsky(e) => e.to_string(), + _ => self.to_string(), }; let mut map = serializer.serialize_map(Some(2))?; map.serialize_entry("error", &format!("{}: {}", self, &error_message))?; @@ -305,3 +309,19 @@ impl<'r> FromRequest<'r> for BotCheck { Outcome::Success(Self(false)) } } + +#[derive(Serialize, Deserialize)] +pub struct PlcService { + pub id: String, + #[serde(rename = "type")] + pub type_: String, + + #[serde(rename = "serviceEndpoint")] + pub service_endpoint: String, +} + +#[derive(Serialize, Deserialize)] +pub struct DidPlcResponse { + pub id: String, + pub service: Vec, +}