1use nostr_sdk::prelude::*;
2use serde::Serialize;
3
4use crate::client::{self, RelayResult};
5use crate::config::load_config;
6use crate::error::{AmError, AmResult};
7use crate::identity::load_keys;
8
9#[derive(Debug, Serialize)]
10pub struct ProfileInfo {
11 pub npub: String,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub name: Option<String>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub about: Option<String>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub picture: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub website: Option<String>,
20 pub event_id: String,
21 #[serde(skip_serializing_if = "Vec::is_empty")]
22 pub relays: Vec<RelayResult>,
23}
24
25pub async fn set(
26 identity: Option<&str>,
27 name: Option<&str>,
28 about: Option<&str>,
29 picture: Option<&str>,
30 website: Option<&str>,
31 passphrase: Option<&str>,
32 verbosity: u8,
33) -> AmResult<ProfileInfo> {
34 if name.is_none() && about.is_none() && picture.is_none() && website.is_none() {
35 return Err(AmError::Args(
36 "at least one of --name, --about, --picture, or --website is required".into(),
37 ));
38 }
39
40 let keys = load_keys(identity.unwrap_or("default"), passphrase)?;
41 let config = load_config()?;
42
43 if config.relays.is_empty() {
44 return Err(AmError::Network(
45 "no relays configured; run `am relay add <url>`".into(),
46 ));
47 }
48
49 let mut metadata = Metadata::new();
50 if let Some(n) = name {
51 metadata.name = Some(n.to_string());
52 }
53 if let Some(a) = about {
54 metadata.about = Some(a.to_string());
55 }
56 if let Some(p) = picture {
57 metadata.picture = Some(p.to_string());
58 }
59 if let Some(w) = website {
60 metadata.website = Some(w.to_string());
61 }
62
63 let npub = keys.public_key().to_bech32().unwrap_or_default();
64 let client = client::connect(keys, &config.relays).await?;
65
66 let event = client
68 .sign_event_builder(EventBuilder::metadata(&metadata))
69 .await
70 .map_err(|e| AmError::Network(e.to_string()))?;
71
72 let event_id = event.id.to_bech32().unwrap_or_default();
73
74 let (relay_results, _succeeded) =
75 client::send_with_retry(&client, &event, &config.relays, 3, verbosity).await;
76
77 client.disconnect().await;
78
79 Ok(ProfileInfo {
80 npub,
81 name: name.map(String::from),
82 about: about.map(String::from),
83 picture: picture.map(String::from),
84 website: website.map(String::from),
85 event_id,
86 relays: relay_results,
87 })
88}