mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <wpfleger@block.xyz> Signed-off-by: Will Pfleger <wpfleger@squareup.com> Signed-off-by: Will Pfleger <wpfleger96@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1fgdl5qqnh3k3f2xkqrvt7cujalhm623x4s7fdjdj5yrtp5fzjl9qrjpucw <4a1bfa0013bc6d14a8d600d8bf6392efefbd2a26ac3c96c9b2a106b0d12297ca@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub16v54tttfqacx9ycvc3k0ut0npj564ahcuajzy6qjvh57ntmsf4uq4806j2 <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
141 lines
5.6 KiB
Rust
141 lines
5.6 KiB
Rust
use uuid::Uuid;
|
|
|
|
use crate::client::{extract_d_tag, normalize_write_response, BuzzClient};
|
|
use crate::error::CliError;
|
|
use crate::validate::{parse_uuid, sdk_err, validate_hex64};
|
|
|
|
/// List DM conversations by querying kind:41001 (relay-confirmed DMs) filtered by our pubkey.
|
|
pub async fn cmd_list_dms(client: &BuzzClient, limit: Option<u32>) -> Result<(), CliError> {
|
|
let my_pk = client.keys().public_key().to_hex();
|
|
let limit = limit.unwrap_or(50).min(200);
|
|
let filter = serde_json::json!({
|
|
"kinds": [41001],
|
|
"#p": [my_pk],
|
|
"limit": limit
|
|
});
|
|
let resp = client.query(&filter).await?;
|
|
let events: Vec<serde_json::Value> = serde_json::from_str(&resp).unwrap_or_default();
|
|
let dms: Vec<serde_json::Value> = events
|
|
.iter()
|
|
.map(|e| {
|
|
let dm_id = extract_d_tag(e);
|
|
let participants: Vec<String> = e
|
|
.get("tags")
|
|
.and_then(|t| t.as_array())
|
|
.map(|tags| {
|
|
tags.iter()
|
|
.filter_map(|tag| {
|
|
let arr = tag.as_array()?;
|
|
if arr.first()?.as_str()? == "p" {
|
|
arr.get(1)?.as_str().map(|s| s.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect()
|
|
})
|
|
.unwrap_or_default();
|
|
serde_json::json!({
|
|
"dm_id": dm_id,
|
|
"participants": participants,
|
|
"created_at": e.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0),
|
|
})
|
|
})
|
|
.collect();
|
|
let output = serde_json::to_string(&dms).unwrap_or_default();
|
|
println!("{output}");
|
|
Ok(())
|
|
}
|
|
|
|
/// Open a DM with one or more users — sign and submit a kind:41010 event with a d-tag.
|
|
pub async fn cmd_open_dm(client: &BuzzClient, pubkeys: &[String]) -> Result<(), CliError> {
|
|
if pubkeys.is_empty() || pubkeys.len() > 8 {
|
|
return Err(CliError::Usage("--pubkey: must provide 1-8 pubkeys".into()));
|
|
}
|
|
for pk in pubkeys {
|
|
validate_hex64(pk)?;
|
|
}
|
|
let dm_id = Uuid::new_v4().to_string();
|
|
let refs: Vec<&str> = pubkeys.iter().map(String::as_str).collect();
|
|
|
|
// build_dm_open doesn't accept a d-tag, so we build the event manually
|
|
// using the SDK builder and add the d-tag ourselves.
|
|
use nostr::{EventBuilder, Kind, Tag};
|
|
let mut tags: Vec<Tag> = refs
|
|
.iter()
|
|
.map(|pk| Tag::parse(["p", *pk]).map_err(|e| CliError::Other(format!("tag error: {e}"))))
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
tags.push(Tag::parse(["d", &dm_id]).map_err(|e| CliError::Other(format!("tag error: {e}")))?);
|
|
let builder = EventBuilder::new(Kind::Custom(41010), "").tags(tags);
|
|
let event = client.sign_event(builder)?;
|
|
|
|
let resp = client.submit_event(event).await?;
|
|
// Try to extract relay-assigned channel_id from response message.
|
|
// Relay returns: {"event_id":"...","accepted":true,"message":"response:{\"channel_id\":\"...\",\"created\":true}"}
|
|
let relay_dm_id = serde_json::from_str::<serde_json::Value>(&resp)
|
|
.ok()
|
|
.and_then(|v| v.get("message")?.as_str().map(|s| s.to_string()))
|
|
.and_then(|msg| {
|
|
let json_part = msg.strip_prefix("response:")?;
|
|
serde_json::from_str::<serde_json::Value>(json_part).ok()
|
|
})
|
|
.and_then(|v| v.get("channel_id")?.as_str().map(|s| s.to_string()));
|
|
let final_dm_id = relay_dm_id.unwrap_or(dm_id);
|
|
|
|
let mut normalized: serde_json::Value =
|
|
serde_json::from_str(&resp).unwrap_or(serde_json::json!({}));
|
|
normalized["dm_id"] = serde_json::json!(final_dm_id);
|
|
if normalized.get("accepted").is_none() {
|
|
normalized["accepted"] = serde_json::json!(true);
|
|
}
|
|
println!("{normalized}");
|
|
Ok(())
|
|
}
|
|
|
|
/// Hide a DM channel — sign and submit a kind:41012 event with h-tag.
|
|
pub async fn cmd_hide_dm(client: &BuzzClient, channel_id: &str) -> Result<(), CliError> {
|
|
let channel_uuid = parse_uuid(channel_id)?;
|
|
|
|
use nostr::{EventBuilder, Kind, Tag};
|
|
let tags = vec![Tag::parse(["h", &channel_uuid.to_string()])
|
|
.map_err(|e| CliError::Other(format!("tag error: {e}")))?];
|
|
let builder =
|
|
EventBuilder::new(Kind::Custom(buzz_sdk::kind::KIND_DM_HIDE as u16), "").tags(tags);
|
|
let event = client.sign_event(builder)?;
|
|
|
|
let resp = client.submit_event(event).await?;
|
|
println!("{}", normalize_write_response(&resp));
|
|
Ok(())
|
|
}
|
|
|
|
/// Add a member to a DM group — sign and submit a kind:41011 event.
|
|
pub async fn cmd_add_dm_member(
|
|
client: &BuzzClient,
|
|
channel_id: &str,
|
|
pubkey: &str,
|
|
) -> Result<(), CliError> {
|
|
let channel_uuid = parse_uuid(channel_id)?;
|
|
validate_hex64(pubkey)?;
|
|
|
|
let builder = buzz_sdk::build_dm_add_member(channel_uuid, pubkey).map_err(sdk_err)?;
|
|
let event = client.sign_event(builder)?;
|
|
|
|
let resp = client.submit_event(event).await?;
|
|
println!("{}", normalize_write_response(&resp));
|
|
Ok(())
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Dispatch
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub async fn dispatch(cmd: crate::DmsCmd, client: &BuzzClient) -> Result<(), CliError> {
|
|
use crate::DmsCmd;
|
|
match cmd {
|
|
DmsCmd::List { limit } => cmd_list_dms(client, limit).await,
|
|
DmsCmd::Open { pubkeys } => cmd_open_dm(client, &pubkeys).await,
|
|
DmsCmd::AddMember { channel, pubkey } => cmd_add_dm_member(client, &channel, &pubkey).await,
|
|
DmsCmd::Hide { channel } => cmd_hide_dm(client, &channel).await,
|
|
}
|
|
}
|