1use clap::Parser;
2use jacquard::api::app_bsky::actor::{AdultContentPref, PreferencesItem};
3use jacquard::client::vec_update::VecUpdate;
4use jacquard::client::{Agent, FileAuthStore};
5use jacquard::oauth::atproto::AtprotoClientMetadata;
6use jacquard::oauth::client::OAuthClient;
7use jacquard::oauth::loopback::LoopbackConfig;
8use jacquard::{CowStr, IntoStatic};
9
10#[derive(Parser, Debug)]
11#[command(author, version, about = "Update Bluesky preferences")]
12struct Args {
13 /// Handle (e.g., alice.bsky.social), DID, or PDS URL
14 input: CowStr<'static>,
15
16 /// Enable adult content
17 #[arg(long)]
18 enable_adult_content: bool,
19
20 /// Path to auth store file (will be created if missing)
21 #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")]
22 store: String,
23}
24
25/// Helper struct for the VecUpdate pattern on preferences
26pub struct PreferencesUpdate;
27
28impl VecUpdate for PreferencesUpdate {
29 type GetRequest<'de> = jacquard::api::app_bsky::actor::get_preferences::GetPreferences;
30 type PutRequest<'de> = jacquard::api::app_bsky::actor::put_preferences::PutPreferences<'de>;
31 type Item = PreferencesItem<'static>;
32
33 fn build_get<'s>() -> Self::GetRequest<'s> {
34 jacquard::api::app_bsky::actor::get_preferences::GetPreferences::new().build()
35 }
36
37 fn build_put<'s>(items: Vec<Self::Item>) -> Self::PutRequest<'s> {
38 jacquard::api::app_bsky::actor::put_preferences::PutPreferences {
39 preferences: items,
40 extra_data: Default::default(),
41 }
42 }
43
44 fn extract_vec(
45 output: jacquard::api::app_bsky::actor::get_preferences::GetPreferencesOutput<'_>,
46 ) -> Vec<Self::Item> {
47 output
48 .preferences
49 .into_iter()
50 .map(|p| p.into_static())
51 .collect()
52 }
53
54 fn matches(a: &Self::Item, b: &Self::Item) -> bool {
55 // Match by enum variant discriminant
56 std::mem::discriminant(a) == std::mem::discriminant(b)
57 }
58}
59
60#[tokio::main]
61async fn main() -> miette::Result<()> {
62 let args = Args::parse();
63
64 let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store));
65 let session = oauth
66 .login_with_local_server(args.input, Default::default(), LoopbackConfig::default())
67 .await?;
68
69 let agent: Agent<_> = Agent::from(session);
70
71 // Create the adult content preference
72 let adult_pref = AdultContentPref {
73 enabled: args.enable_adult_content,
74 extra_data: Default::default(),
75 };
76
77 // Update preferences using update_vec_item
78 // This will replace existing AdultContentPref or add it if not present
79 agent
80 .update_vec_item::<PreferencesUpdate>(PreferencesItem::AdultContentPref(Box::new(
81 adult_pref,
82 )))
83 .await?;
84
85 println!(
86 "✓ Updated adult content preference: {}",
87 if args.enable_adult_content {
88 "enabled"
89 } else {
90 "disabled"
91 }
92 );
93
94 Ok(())
95}