1use clap::Parser;
2use jacquard::CowStr;
3use jacquard::api::app_bsky::actor::profile::Profile;
4use jacquard::client::{Agent, AgentSessionExt, FileAuthStore};
5use jacquard::oauth::client::OAuthClient;
6use jacquard::oauth::loopback::LoopbackConfig;
7use jacquard::types::string::AtUri;
8
9#[derive(Parser, Debug)]
10#[command(author, version, about = "Update profile display name and description")]
11struct Args {
12 /// Handle (e.g., alice.bsky.social), DID, or PDS URL
13 input: CowStr<'static>,
14
15 /// New display name
16 #[arg(long)]
17 display_name: Option<String>,
18
19 /// New bio/description
20 #[arg(long)]
21 description: Option<String>,
22
23 /// Path to auth store file (will be created if missing)
24 #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")]
25 store: String,
26}
27
28#[tokio::main]
29async fn main() -> miette::Result<()> {
30 let args = Args::parse();
31
32 let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store));
33 let session = oauth
34 .login_with_local_server(args.input, Default::default(), LoopbackConfig::default())
35 .await?;
36
37 let agent: Agent<_> = Agent::from(session);
38
39 // Get session info to build the at:// URI for the profile record
40 let (did, _) = agent
41 .info()
42 .await
43 .ok_or_else(|| miette::miette!("No session info available"))?;
44
45 // Profile records use "self" as the rkey
46 let uri_string = format!("at://{}/app.bsky.actor.profile/self", did);
47 let uri = AtUri::new(&uri_string)?;
48
49 // Update profile in-place using the fetch-modify-put pattern
50 agent
51 .update_record::<Profile>(uri, |profile| {
52 if let Some(name) = &args.display_name {
53 profile.display_name = Some(CowStr::from(name.clone()));
54 }
55 if let Some(desc) = &args.description {
56 profile.description = Some(CowStr::from(desc.clone()));
57 }
58 })
59 .await?;
60
61 println!("✓ Profile updated successfully");
62
63 Ok(())
64}