v2_typing_probe/
v2_typing_probe.rs1use nostr_sdk::prelude::*;
12use vector_sdk::VectorBot;
13
14#[tokio::main]
15async fn main() -> vector_sdk::Result<()> {
16 let channel_name = std::env::args().nth(1).unwrap_or_else(|| "spam and testing".into());
17
18 let data_dir = std::env::temp_dir().join("v2_send_once_data");
19 let bot = VectorBot::builder().data_dir(&data_dir).build().await?;
20 println!("── probe online as {}", bot.npub());
21
22 let (mut community_hex, mut channel_hex) = (None::<String>, None::<String>);
24 for c in bot.core().list_communities().await {
25 if c.get("version").and_then(|v| v.as_u64()) != Some(2) {
26 continue;
27 }
28 let cid = c.get("community_id").and_then(|v| v.as_str()).map(String::from);
29 for ch in c.get("channels").and_then(|v| v.as_array()).cloned().unwrap_or_default() {
30 if ch.get("name").and_then(|n| n.as_str()) == Some(channel_name.as_str()) {
31 community_hex = cid.clone();
32 channel_hex = ch.get("channel_id").and_then(|i| i.as_str()).map(String::from);
33 }
34 }
35 }
36 let (community_hex, channel_hex) = match (community_hex, channel_hex) {
37 (Some(c), Some(ch)) => (c, ch),
38 _ => {
39 eprintln!("!! channel \"{channel_name}\" not held locally — join first (run v2_send_once once)");
40 std::process::exit(1);
41 }
42 };
43
44 let cid = vector_core::community::CommunityId(vector_core::simd::hex::hex_to_bytes_32(&community_hex));
47 let community = vector_core::db::community::load_community_v2(&cid)
48 .map_err(vector_core::VectorError::Other)?
49 .ok_or(vector_core::VectorError::Other("community row missing".into()))?;
50 let chid = vector_core::community::ChannelId(vector_core::simd::hex::hex_to_bytes_32(&channel_hex));
51 let ch = community.channel(&chid).ok_or(vector_core::VectorError::Other("channel missing".into()))?;
52 let (secret, epoch) = community.channel_secret(ch);
53 let plane_pk = vector_core::community::v2::derive::channel_group_key(&secret, &chid, epoch).pk();
54 println!("── channel \"{channel_name}\" plane pk = {plane_pk}");
55 println!("── community relays: {:?}", community.relays);
56
57 let group = vector_core::community::v2::derive::channel_group_key(&secret, &chid, epoch);
62 for relay in community.relays.clone() {
63 let plane_pk = plane_pk;
64 let group = group.clone();
65 tokio::spawn(async move {
66 let client = Client::default();
67 if client.add_relay(&relay).await.is_err() {
68 println!("[{relay}] add failed");
69 return;
70 }
71 client.connect().await;
72 let filter = Filter::new().kind(Kind::Custom(21059)).author(plane_pk);
73 if let Err(e) = client.subscribe(filter, None).await {
74 println!("[{relay}] subscribe failed: {e}");
75 return;
76 }
77 println!("[{relay}] subscribed");
78 let mut notifications = client.notifications();
79 while let Ok(n) = notifications.recv().await {
80 if let RelayPoolNotification::Event { event, .. } = n {
81 println!(
82 "[{relay}] ← 21059 wrap id={} wrap_created_at={} size={}B",
83 &event.id.to_hex()[..12],
84 event.created_at,
85 event.content.len()
86 );
87 match vector_core::community::v2::stream::open_wrap(&event, &group) {
88 Ok(opened) => {
89 let now = std::time::SystemTime::now()
90 .duration_since(std::time::UNIX_EPOCH)
91 .map(|d| d.as_millis() as u64)
92 .unwrap_or(0);
93 let age = now as i128 - opened.at_ms as i128;
94 println!(
95 " rumor: kind={} author={} created_at={} content={:?}",
96 opened.rumor.kind,
97 &opened.author.to_hex()[..12],
98 opened.rumor.created_at,
99 opened.rumor.content
100 );
101 println!(" tags: {:?}", opened.rumor.tags.iter().map(|t| t.as_slice().to_vec()).collect::<Vec<_>>());
102 println!(" seal: kind={} created_at={}", opened.seal.kind, opened.seal.created_at);
103 println!(" at_ms={} (age {age} ms → Armada freshness gate {} 8000ms)", opened.at_ms, if age <= 8000 { "PASSES ≤" } else { "FAILS >" });
104 }
105 Err(e) => println!(" !! open failed: {e}"),
106 }
107 }
108 }
109 });
110 }
111 tokio::time::sleep(std::time::Duration::from_secs(4)).await;
112
113 println!("── sending !typing into \"{channel_name}\"…");
115 match bot.channel(&channel_hex).send("!typing").await {
116 Ok(_) => println!("── trigger sent; watching 25s for the bot's typing wrap…"),
117 Err(e) => eprintln!("!! trigger send failed: {e}"),
118 }
119 tokio::time::sleep(std::time::Duration::from_secs(25)).await;
120 println!("── probe done");
121 Ok(())
122}