nostr_client.rs (10689B)
1 use std::collections::HashMap; 2 use std::sync::{Arc, Mutex}; 3 4 use crate::events::Event; 5 use crate::req::{Req, ReqFilter}; 6 use crate::websocket::SimplifiedWS; 7 use serde_json::{json, Value}; 8 use tungstenite::Message; 9 10 /// Nostr Client 11 pub struct Client { 12 pub relays: HashMap<String, Arc<Mutex<SimplifiedWS>>>, 13 pub subscriptions: HashMap<String, Vec<Message>>, 14 } 15 16 impl Client { 17 /// Create a new client with a list of default relays 18 /// 19 /// # Example 20 /// ```rust 21 /// use nostr_rust::nostr_client::Client; 22 /// let client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 23 /// ``` 24 pub fn new(default_relays: Vec<&str>) -> Result<Self, String> { 25 let mut client = Self { 26 relays: HashMap::new(), 27 subscriptions: HashMap::new(), 28 }; 29 30 for relay in default_relays { 31 client.add_relay(relay)?; 32 } 33 34 Ok(client) 35 } 36 } 37 38 impl Client { 39 /// Add a relay to the client 40 /// # Example 41 /// ```rust 42 /// use nostr_rust::nostr_client::Client; 43 /// let mut client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 44 /// client.add_relay("wss://relay.damus.io").unwrap(); 45 /// ``` 46 pub fn add_relay(&mut self, relay: &str) -> Result<(), String> { 47 let client = match SimplifiedWS::new(relay) { 48 Ok(client) => client, 49 Err(err) => return Err(format!("Error connecting to relay: {}", err)), 50 }; 51 52 // Check if relay is already added 53 if self.relays.contains_key(relay) { 54 return Err(format!("Relay {} already added", relay)); 55 } 56 57 self.relays 58 .insert(relay.to_string(), Arc::new(Mutex::new(client))); 59 60 Ok(()) 61 } 62 63 /// Remove a relay from the client 64 /// # Example 65 /// ```rust 66 /// use nostr_rust::nostr_client::Client; 67 /// let mut client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 68 /// client.remove_relay("wss://nostr-pub.wellorder.net").unwrap(); 69 /// ``` 70 pub fn remove_relay(&mut self, relay: &str) -> Result<(), String> { 71 println!("Removing relay {}", relay); 72 if !self.relays.contains_key(relay) { 73 println!("Relay {} not found", relay); 74 return Err(format!("Relay {} not found", relay)); 75 } 76 77 println!("Removing relay {}", relay); 78 79 // Close the connection 80 self.relays 81 .remove(relay) 82 .unwrap() 83 .lock() 84 .unwrap() 85 .socket 86 .close(None) 87 .unwrap(); 88 89 Ok(()) 90 } 91 92 /// Publish a Nostr event 93 pub fn publish_event(&mut self, event: &Event) -> Result<(), String> { 94 let json_stringified = json!(["EVENT", event]).to_string(); 95 let message = Message::text(json_stringified); 96 97 for relay in self.relays.values() { 98 let mut relay = relay.lock().unwrap(); 99 relay.send_message(&message)?; 100 } 101 102 Ok(()) 103 } 104 105 /// Get next data from the relays 106 /// # Example 107 /// ```rust 108 /// use std::{ 109 /// sync::{Arc, Mutex}, 110 /// thread, 111 /// }; 112 /// use tungstenite::Message; 113 /// use nostr_rust::{nostr_client::Client, req::ReqFilter}; 114 /// 115 /// fn handle_message(relay_url: &String, message: &Message) -> Result<(), String> { 116 /// println!("Received message: {:?}", message); 117 /// 118 /// Ok(()) 119 /// } 120 /// 121 /// let mut client = Arc::new(Mutex::new(Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap())); 122 /// 123 /// // Run a new thread to listen 124 /// let nostr_clone = client.clone(); 125 /// let nostr_thread = thread::spawn(move || loop { 126 /// let events = nostr_clone.lock().unwrap().next_data().unwrap(); 127 /// 128 /// for (relay_url, message) in events.iter() { 129 /// handle_message(relay_url, message).unwrap(); 130 /// } 131 /// }); 132 /// 133 /// // Subscribe to the most beautiful Nostr profile event 134 /// client 135 /// .lock() 136 /// .unwrap() 137 /// .subscribe(vec![ReqFilter { 138 /// ids: None, 139 /// authors: Some(vec![ 140 /// "884704bd421721e292edbff42eb77547fe115c6ff9825b08fc366be4cd69e9f6".to_string(), 141 /// ]), 142 /// kinds: None, 143 /// e: None, 144 /// p: None, 145 /// since: None, 146 /// until: None, 147 /// limit: Some(1), 148 /// }]) 149 /// .unwrap(); 150 /// 151 /// // Wait 3s for the thread to finish 152 /// std::thread::sleep(std::time::Duration::from_secs(3)); 153 /// ``` 154 pub fn next_data(&mut self) -> Result<Vec<(String, tungstenite::Message)>, String> { 155 let mut events: Vec<(String, tungstenite::Message)> = Vec::new(); 156 157 for (relay_name, socket) in self.relays.iter() { 158 let message = socket.lock().unwrap().read_message()?; 159 events.push((relay_name.clone(), message)); 160 } 161 162 Ok(events) 163 } 164 165 /// Subscribe 166 /// # Example 167 /// ```rust 168 /// use nostr_rust::{nostr_client::Client, req::ReqFilter}; 169 /// let mut client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 170 /// client 171 /// .subscribe(vec![ReqFilter { // None means generate a random ID 172 /// ids: None, 173 /// authors: Some(vec![ 174 /// "884704bd421721e292edbff42eb77547fe115c6ff9825b08fc366be4cd69e9f6".to_string(), 175 /// ]), 176 /// kinds: None, 177 /// e: None, 178 /// p: None, 179 /// since: None, 180 /// until: None, 181 /// limit: Some(1), 182 /// }]) 183 /// .unwrap(); 184 /// ``` 185 pub fn subscribe(&mut self, filters: Vec<ReqFilter>) -> Result<String, String> { 186 let req = Req::new(None, filters); 187 let message = Message::text(req.to_string()); 188 189 for relay in self.relays.values() { 190 let mut relay = relay.lock().unwrap(); 191 relay.send_message(&message)?; 192 } 193 194 Ok(req.subscription_id) 195 } 196 197 /// Subscribe with a specific ID 198 /// 199 /// # Example 200 /// ```rust 201 /// use nostr_rust::{nostr_client::Client, req::ReqFilter}; 202 /// let mut client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 203 /// client 204 /// .subscribe_with_id("my_subscription_id", vec![ReqFilter { 205 /// ids: None, 206 /// authors: Some(vec![ 207 /// "884704bd421721e292edbff42eb77547fe115c6ff9825b08fc366be4cd69e9f6".to_string(), 208 /// ]), 209 /// kinds: None, 210 /// e: None, 211 /// p: None, 212 /// since: None, 213 /// until: None, 214 /// limit: Some(1), 215 /// }]) 216 /// .unwrap(); 217 /// ``` 218 pub fn subscribe_with_id( 219 &mut self, 220 subscription_id: &str, 221 filters: Vec<ReqFilter>, 222 ) -> Result<(), String> { 223 let req = Req::new(Some(subscription_id), filters); 224 let message = Message::text(req.to_string()); 225 226 for relay in self.relays.values() { 227 let mut relay = relay.lock().unwrap(); 228 relay.send_message(&message)?; 229 } 230 231 Ok(()) 232 } 233 234 /// Unsubscribe 235 /// # Example 236 /// ```rust 237 /// use nostr_rust::{nostr_client::Client, req::ReqFilter}; 238 /// let mut client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 239 /// let subscription_id = client 240 /// .subscribe(vec![ReqFilter { 241 /// ids: None, 242 /// authors: Some(vec![ 243 /// "884704bd421721e292edbff42eb77547fe115c6ff9825b08fc366be4cd69e9f6".to_string(), 244 /// ]), 245 /// kinds: None, 246 /// e: None, 247 /// p: None, 248 /// since: None, 249 /// until: None, 250 /// limit: Some(1), 251 /// }]) 252 /// .unwrap(); 253 /// client.unsubscribe(&subscription_id).unwrap(); 254 /// ``` 255 pub fn unsubscribe(&mut self, subscription_id: &str) -> Result<(), String> { 256 let message = Message::text(json!(["CLOSE", subscription_id]).to_string()); 257 258 for relay in self.relays.values() { 259 let mut relay = relay.lock().unwrap(); 260 relay.send_message(&message)?; 261 } 262 263 Ok(()) 264 } 265 266 /// Add event to a subscription 267 pub fn add_event(&mut self, subscription_id: &str, message: Message) { 268 // Check if the subscription exists 269 if !self.subscriptions.contains_key(subscription_id) { 270 self.subscriptions 271 .insert(subscription_id.to_string(), Vec::new()); 272 } 273 274 // Check if the message is already in the subscription 275 if !self.subscriptions[subscription_id].contains(&message) { 276 // Add the message to the subscription 277 self.subscriptions 278 .get_mut(subscription_id) 279 .unwrap() 280 .push(message); 281 } 282 } 283 284 /// Get events and remove them from the subscription 285 pub fn get_events(&mut self, subscription_id: &str) -> Option<Vec<Message>> { 286 self.subscriptions.remove(subscription_id) 287 } 288 289 /// Get events of a given filters 290 /// 291 /// # Example 292 /// ```rust 293 /// use nostr_rust::{nostr_client::Client, req::ReqFilter}; 294 /// let mut client = Client::new(vec!["wss://nostr-pub.wellorder.net"]).unwrap(); 295 /// let events = client.get_events_of(vec![ReqFilter { 296 /// ids: None, 297 /// authors: Some(vec!["884704bd421721e292edbff42eb77547fe115c6ff9825b08fc366be4cd69e9f6".to_string()]), 298 /// kinds: Some(vec![3]), 299 /// e: None, 300 /// p: None, 301 /// since: None, 302 /// until: None, 303 /// limit: Some(1), 304 /// }]).unwrap(); 305 /// ``` 306 pub fn get_events_of(&mut self, filters: Vec<ReqFilter>) -> Result<Vec<Event>, String> { 307 let mut events: Vec<Event> = Vec::new(); 308 309 // Subscribe 310 let id = self.subscribe(filters)?; 311 312 // Get the events 313 loop { 314 let data = self.next_data()?; 315 let mut break_loop = false; 316 317 for (_, message) in data { 318 let event: Value = serde_json::from_str(&message.to_string()).unwrap(); 319 320 if event[0] == "EOSE" && event[1].as_str() == Some(&id) { 321 break_loop = true; 322 break; 323 } 324 325 self.add_event(&id, message); 326 } 327 328 if break_loop { 329 break; 330 } 331 } 332 333 // unsubscribe 334 self.unsubscribe(&id).unwrap(); 335 336 // Get the events 337 if let Some(messages) = self.get_events(&id) { 338 for message in messages { 339 let event: Value = serde_json::from_str(&message.to_string()).unwrap(); 340 let event_object: Event = serde_json::from_value(event[2].clone()).unwrap(); 341 events.push(event_object); 342 } 343 } 344 Ok(events) 345 } 346 }