A better Rust ATProto crate
1use clap::Parser; 2use jacquard::CowStr; 3use jacquard::api::com_whtwnd::blog::entry::Entry; 4use jacquard::client::{Agent, FileAuthStore}; 5use jacquard::oauth::atproto::AtprotoClientMetadata; 6use jacquard::oauth::client::OAuthClient; 7use jacquard::oauth::loopback::LoopbackConfig; 8use jacquard::types::string::Datetime; 9use jacquard::xrpc::XrpcClient; 10use miette::IntoDiagnostic; 11use url::Url; 12 13#[derive(Parser, Debug)] 14#[command(author, version, about = "Create a WhiteWind blog post")] 15struct Args { 16 /// Handle (e.g., alice.bsky.social), DID, or PDS URL 17 input: CowStr<'static>, 18 19 /// Blog post title 20 #[arg(short, long)] 21 title: String, 22 23 /// Blog post content (markdown) 24 #[arg(short, long)] 25 content: String, 26 27 /// Optional subtitle 28 #[arg(short, long)] 29 subtitle: Option<String>, 30 31 /// Path to auth store file (will be created if missing) 32 #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")] 33 store: String, 34} 35 36#[tokio::main] 37async fn main() -> miette::Result<()> { 38 let args = Args::parse(); 39 40 let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store)); 41 let session = oauth 42 .login_with_local_server(args.input, Default::default(), LoopbackConfig::default()) 43 .await?; 44 45 let agent: Agent<_> = Agent::from(session); 46 47 // Create a WhiteWind blog entry 48 // The content field accepts markdown 49 let entry = Entry { 50 title: Some(CowStr::from(args.title)), 51 subtitle: args.subtitle.map(CowStr::from), 52 content: CowStr::from(args.content), 53 created_at: Some(Datetime::now()), 54 visibility: Some(CowStr::from("url")), // "url" = public with link, "author" = public on profile 55 theme: None, 56 ogp: None, 57 blobs: None, 58 is_draft: None, 59 extra_data: Default::default(), 60 }; 61 62 let mut output = agent.create_record(entry, None).await?; 63 println!("Created WhiteWind blog post: {}", output.uri); 64 let url = Url::parse(format!( 65 "https://whtwnd.nat.vg/{}/{}", 66 output.uri.authority(), 67 output.uri.rkey().map(|r| r.as_ref()).unwrap_or("") 68 )) 69 .into_diagnostic()?; 70 println!("View at: {}", url); 71 72 Ok(()) 73}