mirror of
https://github.com/block/buzz.git
synced 2026-11-02 15:31:27 +00:00
## Summary
- preserve string Nostr `sig` values in normal `messages get`, `messages
thread`, `messages search`, and `feed get` output
- omit absent or non-string signatures instead of fabricating empty
values
- keep compact output at exactly `{id, content, created_at}` and update
CLI/managed-agent contract docs
## Validation
- `cargo test -p buzz-cli --lib` (367 passed at final head
`a2498faa0e07f90efc0ab0165606ba3b048327c7`)
- release candidate live-relay matrix: 49/49 normal events independently
recomputed to their NIP-01 IDs and passed BIP-340 verification; all four
compact commands preserved exact three-key output and ordering
- `git diff origin/main...HEAD --check`
- independent adversarial review: clear, no findings
## Compatibility
The normal JSON change is additive for valid relay events. In-repo
consumers use tolerant field lookup. Compact output is unchanged and
locked by regressions for both message and feed formatting.
Generated by Carl, an automated contributor working with Wes.
---------
Signed-off-by: Carl <9d00794d3df50972eb8b615511783cab12a77a8fd5dd5edd58073ec73b54bd8b@buzz.block.builderlab.xyz>
Co-authored-by: Carl <9d00794d3df50972eb8b615511783cab12a77a8fd5dd5edd58073ec73b54bd8b@buzz.block.builderlab.xyz>
116 lines
3.6 KiB
Rust
116 lines
3.6 KiB
Rust
use std::cmp::Reverse;
|
|
|
|
use crate::client::{normalize_events, BuzzClient};
|
|
use crate::error::CliError;
|
|
|
|
const VALID_FEED_TYPES: &[&str] = &["mentions", "needs_action", "activity", "agent_activity"];
|
|
|
|
fn format_events(normalized: &str, format: &crate::OutputFormat) -> String {
|
|
match format {
|
|
crate::OutputFormat::Compact => {
|
|
let events: Vec<serde_json::Value> =
|
|
serde_json::from_str(normalized).unwrap_or_default();
|
|
let compact: Vec<serde_json::Value> = events
|
|
.iter()
|
|
.map(|e| {
|
|
serde_json::json!({
|
|
"id": e.get("id").cloned().unwrap_or_default(),
|
|
"content": e.get("content").cloned().unwrap_or_default(),
|
|
"created_at": e.get("created_at").cloned().unwrap_or_default(),
|
|
})
|
|
})
|
|
.collect();
|
|
serde_json::to_string(&compact).unwrap_or_default()
|
|
}
|
|
crate::OutputFormat::Json => normalized.to_string(),
|
|
}
|
|
}
|
|
|
|
/// Get activity feed — query events mentioning our pubkey (via p-tag).
|
|
pub async fn cmd_get_feed(
|
|
client: &BuzzClient,
|
|
since: Option<i64>,
|
|
limit: Option<u32>,
|
|
types: Option<&str>,
|
|
format: &crate::OutputFormat,
|
|
) -> Result<(), CliError> {
|
|
let my_pk = client.keys().public_key().to_hex();
|
|
let limit = limit.unwrap_or(20).min(50);
|
|
|
|
let mut filter = serde_json::json!({
|
|
"#p": [my_pk],
|
|
"limit": limit
|
|
});
|
|
|
|
if let Some(s) = since {
|
|
filter["since"] = serde_json::json!(s);
|
|
}
|
|
|
|
if let Some(types_str) = types {
|
|
let type_list: Vec<&str> = types_str.split(',').map(str::trim).collect();
|
|
for t in &type_list {
|
|
if !VALID_FEED_TYPES.contains(t) {
|
|
return Err(crate::error::CliError::Usage(format!(
|
|
"invalid feed type {t:?} — must be one of: {}",
|
|
VALID_FEED_TYPES.join(", ")
|
|
)));
|
|
}
|
|
}
|
|
filter["feed_types"] = serde_json::json!(type_list);
|
|
}
|
|
|
|
let resp = client.query(&filter).await?;
|
|
let mut events: Vec<serde_json::Value> = serde_json::from_str(&resp).unwrap_or_default();
|
|
events.sort_by_key(|e| Reverse(e.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0)));
|
|
let normalized = normalize_events(&events);
|
|
println!("{}", format_events(&normalized, format));
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn dispatch(
|
|
cmd: crate::FeedCmd,
|
|
client: &BuzzClient,
|
|
format: &crate::OutputFormat,
|
|
) -> Result<(), CliError> {
|
|
use crate::FeedCmd;
|
|
match cmd {
|
|
FeedCmd::Get {
|
|
since,
|
|
limit,
|
|
types,
|
|
} => cmd_get_feed(client, since, limit, types.as_deref(), format).await,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::format_events;
|
|
|
|
#[test]
|
|
fn compact_event_format_remains_the_three_key_contract() {
|
|
let normalized = serde_json::json!([{
|
|
"id": "a".repeat(64),
|
|
"pubkey": "b".repeat(64),
|
|
"kind": 9,
|
|
"content": "compact content",
|
|
"created_at": 1_787_754_972_u64,
|
|
"tags": [["p", "c".repeat(64)]],
|
|
"sig": "d".repeat(128),
|
|
}])
|
|
.to_string();
|
|
|
|
let output: Vec<serde_json::Value> =
|
|
serde_json::from_str(&format_events(&normalized, &crate::OutputFormat::Compact))
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
output[0],
|
|
serde_json::json!({
|
|
"id": "a".repeat(64),
|
|
"content": "compact content",
|
|
"created_at": 1_787_754_972_u64,
|
|
})
|
|
);
|
|
}
|
|
}
|