1pub use base64::Engine;
2pub use colorful::{Color, Colorful};
3pub use futures_util::stream::FusedStream;
4pub use futures_util::{SinkExt, StreamExt};
5pub use http::Uri;
6pub use lazy_static::lazy_static;
7pub use nostr_types::{
9 ClientMessage, EncryptedPrivateKey, Event, EventKind, Filter, Id, IdHex, KeySigner, PreEvent,
10 RelayMessage, Signer, SubscriptionId, Tag, Unixtime, Why,
11};
12pub use tokio::sync::mpsc::{Receiver, Sender};
13pub use tungstenite::Message;
14pub use zeroize::Zeroize;
15
16pub use lightning;
18
19type Ws =
20 tokio_tungstenite::WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>;
21
22pub mod reflog;
23pub use reflog::{ref_hash_list, ref_hash_list_padded, ref_hash_list_w_commit_message};
24
25pub use relays::{
26 relays, relays_by_nip, relays_offline, relays_online, relays_paid, relays_public,
27};
28
29pub mod watch_list;
30pub use watch_list::*;
31
32pub fn get_relays_by_nip(nip: &str) -> Result<String, &'static str> {
36 let _relays_no_nl = relays_by_nip(nip).unwrap().to_string();
37
38 Ok(relays_by_nip(nip).unwrap().to_string())
39}
40pub fn get_relays() -> Result<String, &'static str> {
43 let _relays_no_nl = relays().unwrap().to_string();
44
45 Ok(format!("{}", relays().unwrap().to_string()))
46}
47pub fn get_relays_online() -> Result<String, &'static str> {
50 let _relays_no_nl = relays_online().unwrap().to_string();
51
52 Ok(format!("{}", relays_online().unwrap().to_string()))
53}
54pub fn get_relays_public() -> Result<String, &'static str> {
57 let _relays_no_nl = relays_public().unwrap().to_string();
58
59 Ok(format!("{}", relays_public().unwrap().to_string()))
60}
61pub fn get_relays_paid() -> Result<String, &'static str> {
64 let _relays_no_nl = relays_paid().unwrap().to_string();
65
66 Ok(format!("{}", relays_paid().unwrap().to_string()))
67}
68pub fn get_relays_offline() -> Result<String, &'static str> {
71 let _relays_no_nl = relays_offline().unwrap().to_string();
72
73 Ok(format!("{}", relays_offline().unwrap().to_string()))
74}
75
76pub fn get_weeble() -> Result<String, &'static str> {
78 let _weeble_no_nl = weeble().unwrap().to_string();
79
80 Ok(format!("{}", weeble().unwrap().to_string()))
81}
82pub fn get_wobble() -> Result<String, &'static str> {
84 let _wobble_no_nl = wobble().unwrap().to_string();
85
86 Ok(format!("{}", wobble().unwrap().to_string()))
87}
88pub fn get_blockheight() -> Result<String, &'static str> {
90 let _blockheight_no_nl = blockheight().unwrap().to_string();
91
92 Ok(format!("{}", blockheight().unwrap().to_string()))
93}
94pub fn get_blockhash() -> Result<String, &'static str> {
96 let _blockhash_no_nl = blockhash().unwrap().to_string();
97
98 Ok(format!("{}", blockhash().unwrap().to_string()))
99}
100
101pub fn hash_list() {
103 let _ = ref_hash_list();
104}
105
106pub fn hash_list_padded() {
108 let _ = ref_hash_list_padded();
109}
110
111pub fn hash_list_w_commit_message() {
113 let _ = ref_hash_list_w_commit_message();
114}
115
116pub struct Config {
118 pub query: String,
120}
121use sha256::digest;
122use std::process;
123impl Config {
125 pub fn build(args: &[String]) -> Result<Config, &'static str> {
127 if args.len() == 1 {
128 println!("{}", digest("".to_string()));
129 process::exit(0);
130 }
131
132 let query = args[1].clone();
133 Ok(Config { query })
134 }
135}
136use std::error::Error;
137pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
139 println!("{}", digest(config.query));
140 Ok(())
141}
142pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
144 let mut results = Vec::new();
145 for line in contents.lines() {
146 println!("{}", line);
148 if line.contains(query) {
149 let val = digest(query);
151 println!("{}", val);
152 results.push(line);
153 }
154 }
155 results
156}
157
158pub fn post_event(url: &str, event: Event) {
160 let (host, uri) = url_to_host_and_uri(url);
161 let wire = event_to_wire(event);
162 post(host, uri, wire)
163}
164use nostr_types::EventV3;
174pub fn post_event_v3(url: &str, event: EventV3) {
176 let (host, uri) = url_to_host_and_uri(url);
177 let wire = event_to_wire(event);
178 post(host, uri, wire)
179}
180
181pub fn print_event(event: &Event) {
183 print!(
184 "{}",
185 serde_json::to_string(event).expect("Cannot serialize event to JSON")
186 );
187}
188
189mod internal;
190use internal::*;
191
192pub mod weeble;
194pub use weeble::weeble;
195
196pub mod wobble;
198pub use wobble::wobble;
199
200pub mod blockhash;
202pub use blockhash::blockhash;
203
204pub mod blockheight;
206pub use blockheight::blockheight;
207
208pub mod hash;
210pub use hash::hash;
211
212pub mod relays;
227
228pub fn fetch_by_filter(url: &str, filter: Filter) -> Vec<Event> {
230 let (host, uri) = url_to_host_and_uri(url);
231 let wire = filters_to_wire(vec![filter]);
232 fetch(host, uri, wire)
233}
234
235pub fn fetch_by_id(url: &str, id: IdHex) -> Option<Event> {
237 let mut filter = Filter::new();
238 filter.add_id(&id);
239 let events = fetch_by_filter(url, filter);
240 if events.is_empty() {
241 None
242 } else {
243 Some(events[0].clone())
244 }
245}
246
247pub fn get_pwd() -> Result<String, &'static str> {
248 let mut no_nl = pwd().unwrap().to_string();
249 no_nl.retain(|c| c != '\n');
250 return Ok(format!("{ }", no_nl));
251}
252
253pub struct Prefixes {
254 from_relay: String,
255 sending: String,
256}
257
258lazy_static! {
259 pub static ref PREFIXES: Prefixes = Prefixes {
260 from_relay: "Relay".color(Color::Blue).to_string(),
261 sending: "Sending".color(Color::MediumPurple).to_string(),
262 };
263}
264
265pub enum Command {
266 PostEvent(Event),
267 Auth(Event),
268 FetchEvents(SubscriptionId, Vec<Filter>),
269 Exit,
270}
271
272pub struct Probe {
273 pub from_main: tokio::sync::mpsc::Receiver<Command>,
274 pub to_main: tokio::sync::mpsc::Sender<RelayMessage>,
275}
276
277impl Probe {
278 pub fn new(
279 from_main: tokio::sync::mpsc::Receiver<Command>,
280 to_main: tokio::sync::mpsc::Sender<RelayMessage>,
281 ) -> Probe {
282 Probe { from_main, to_main }
283 }
284
285 pub async fn connect_and_listen(
286 &mut self,
287 relay_url: &str,
288 ) -> Result<(), Box<dyn std::error::Error>> {
289 let (host, uri) = url_to_host_and_uri(relay_url);
290
291 let key: [u8; 16] = rand::random();
292 let request = http::request::Request::builder()
293 .method("GET")
294 .header("Host", host)
295 .header("Connection", "Upgrade")
296 .header("Upgrade", "websocket")
297 .header("Sec-WebSocket-Version", "13")
298 .header(
299 "Sec-WebSocket-Key",
300 base64::engine::general_purpose::STANDARD.encode(key),
301 )
302 .uri(uri)
303 .body(())?;
304
305 let (mut websocket, _response) = tokio::time::timeout(
306 std::time::Duration::new(5, 0),
307 tokio_tungstenite::connect_async(request),
308 )
309 .await??;
310
311 let mut ping_timer = tokio::time::interval(std::time::Duration::new(15, 0));
312 ping_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
313 ping_timer.tick().await; loop {
316 tokio::select! {
317 _ = ping_timer.tick() => {
318 let msg = Message::Ping(vec![0x1]);
319 self.send(&mut websocket, msg).await?;
320 },
321 local_message = self.from_main.recv() => {
322 match local_message {
323 Some(Command::PostEvent(event)) => {
324 let client_message = ClientMessage::Event(Box::new(event));
325 let wire = serde_json::to_string(&client_message)?;
326 let msg = Message::Text(wire);
327 self.send(&mut websocket, msg).await?;
328 },
329 Some(Command::Auth(event)) => {
330 let client_message = ClientMessage::Auth(Box::new(event));
331 let wire = serde_json::to_string(&client_message)?;
332 let msg = Message::Text(wire);
333 self.send(&mut websocket, msg).await?;
334 },
335 Some(Command::FetchEvents(subid, filters)) => {
336 let client_message = ClientMessage::Req(subid, filters);
337 let wire = serde_json::to_string(&client_message)?;
338 let msg = Message::Text(wire);
339 self.send(&mut websocket, msg).await?;
340 },
341 Some(Command::Exit) => {
342 break;
343 },
344 None => { }
345 }
346 },
347 message = websocket.next() => {
348 let message = match message {
349 Some(m) => m,
350 None => {
351 if websocket.is_terminated() {
352 eprintln!("{}", "Connection terminated".color(Color::Orange1));
353 }
354 break;
355 }
356 }?;
357
358 Self::display(message.clone())?;
360
361 match message {
363 Message::Text(s) => {
364 let relay_message: RelayMessage = serde_json::from_str(&s)?;
366 self.to_main.send(relay_message).await?;
367 },
368 Message::Binary(_) => { },
369 Message::Ping(_) => { },
370 Message::Pong(_) => { },
371 Message::Close(_) => break,
372 Message::Frame(_) => unreachable!(),
373 }
374 },
375 }
376 }
377
378 let msg = Message::Close(None);
380 self.send(&mut websocket, msg).await?;
381
382 Ok(())
383 }
384
385 fn display(message: Message) -> Result<(), Box<dyn std::error::Error>> {
386 match message {
387 Message::Text(s) => {
388 let relay_message: RelayMessage = serde_json::from_str(&s)?;
389 match relay_message {
390 RelayMessage::Auth(challenge) => {
391 eprintln!("{}: AUTH({})", PREFIXES.from_relay, challenge);
392 }
393 RelayMessage::Event(sub, e) => {
394 let event_json = serde_json::to_string(&e)?;
395 eprintln!(
396 "{}: EVENT({}, {})",
397 PREFIXES.from_relay,
398 sub.as_str(),
399 event_json
400 );
401 }
402 RelayMessage::Closed(sub, msg) => {
403 eprintln!("{}: CLOSED({}, {})", PREFIXES.from_relay, sub.as_str(), msg);
404 }
405 RelayMessage::Notice(s) => {
406 eprintln!("{}: NOTICE({})", PREFIXES.from_relay, s);
407 }
408 RelayMessage::Eose(sub) => {
409 eprintln!("{}: EOSE({})", PREFIXES.from_relay, sub.as_str());
410 }
411 RelayMessage::Ok(id, ok, reason) => {
412 eprintln!(
413 "{}: OK({}, {}, {})",
414 PREFIXES.from_relay,
415 id.as_hex_string(),
416 ok,
417 reason
418 );
419 }
420 RelayMessage::Notify(_) => todo!(),
422 }
423 }
424 Message::Binary(_) => {
425 eprintln!("{}: Binary message received!!!", PREFIXES.from_relay);
426 }
427 Message::Ping(_) => {
428 eprintln!("{}: Ping", PREFIXES.from_relay);
429 }
430 Message::Pong(_) => {
431 eprintln!("{}: Pong", PREFIXES.from_relay);
432 }
433 Message::Close(_) => {
434 eprintln!("{}", "Remote closed nicely.".color(Color::Green));
435 }
436 Message::Frame(_) => {
437 unreachable!()
438 }
439 }
440
441 Ok(())
442 }
443
444 async fn send(
445 &mut self,
446 websocket: &mut Ws,
447 message: Message,
448 ) -> Result<(), Box<dyn std::error::Error>> {
449 match message {
450 Message::Text(ref s) => eprintln!("{}: Text({})", PREFIXES.sending, s),
451 Message::Binary(_) => eprintln!("{}: Binary(_)", PREFIXES.sending),
452 Message::Ping(_) => eprintln!("{}: Ping(_)", PREFIXES.sending),
453 Message::Pong(_) => eprintln!("{}: Pong(_)", PREFIXES.sending),
454 Message::Close(_) => eprintln!("{}: Close(_)", PREFIXES.sending),
455 Message::Frame(_) => eprintln!("{}: Frame(_)", PREFIXES.sending),
456 }
457 Ok(websocket.send(message).await?)
458 }
459}
460
461pub fn url_to_host_and_uri(url: &str) -> (String, Uri) {
462 let uri: http::Uri = url.parse::<http::Uri>().expect("Could not parse url");
463 let authority = uri.authority().expect("Has no hostname").as_str();
464 let host = authority
465 .find('@')
466 .map(|idx| authority.split_at(idx + 1).1)
467 .unwrap_or_else(|| authority);
468 if host.is_empty() {
469 panic!("URL has empty hostname");
470 }
471 (host.to_owned(), uri)
472}
473
474pub fn load_signer() -> Result<KeySigner, Box<dyn std::error::Error>> {
475 let mut config_dir = match dirs::config_dir() {
476 Some(cd) => cd,
477 None => panic!("No config_dir defined for your operating system"),
478 };
479 config_dir.push("nostr-probe");
480 config_dir.push("epk");
481
482 let epk_bytes = match std::fs::read(&config_dir) {
483 Ok(bytes) => bytes,
484 Err(e) => {
485 eprintln!("{}", e);
486 panic!(
487 "Could not find your encrypted private key in {}",
488 config_dir.display()
489 );
490 }
491 };
492 let epk_string = String::from_utf8(epk_bytes)?;
493 let epk = EncryptedPrivateKey(epk_string);
494
495 let mut password = rpassword::prompt_password("Password: ").unwrap();
496 let mut signer = KeySigner::from_encrypted_private_key(epk, &password)?;
497 signer.unlock(&password)?;
498 password.zeroize();
499 Ok(signer)
500}
501
502pub async fn req(
503 relay_url: &str,
504 signer: KeySigner,
505 filter: Filter,
506 to_probe: Sender<Command>,
507 mut from_probe: Receiver<RelayMessage>,
508) -> Result<(), Box<dyn std::error::Error>> {
509 let pubkey = signer.public_key();
510 let mut authenticated: Option<Id> = None;
511
512 let our_sub_id = SubscriptionId("subscription-id".to_string());
513 to_probe
514 .send(Command::FetchEvents(
515 our_sub_id.clone(),
516 vec![filter.clone()],
517 ))
518 .await?;
519
520 loop {
521 let relay_message = from_probe.recv().await.unwrap();
522 let why = relay_message.why();
523 match relay_message {
524 RelayMessage::Auth(challenge) => {
525 let pre_event = PreEvent {
526 pubkey,
527 created_at: Unixtime::now(),
528 kind: EventKind::Auth,
529 tags: vec![
530 Tag::new(&["relay", relay_url]),
531 Tag::new(&["challenge", &challenge]),
532 ],
533 content: "".to_string(),
534 };
535 let event = signer.sign_event(pre_event)?;
536 authenticated = Some(event.id);
537 to_probe.send(Command::Auth(event)).await?;
538 }
539 RelayMessage::Eose(sub) => {
540 if sub == our_sub_id {
541 to_probe.send(Command::Exit).await?;
542 break;
543 }
544 }
545 RelayMessage::Event(sub, e) => {
546 if sub == our_sub_id {
547 println!("{}", serde_json::to_string(&e)?);
548 }
549 }
550 RelayMessage::Closed(sub, _) => {
551 if sub == our_sub_id {
552 if why == Some(Why::AuthRequired) {
553 if authenticated.is_none() {
554 eprintln!("Relay CLOSED our sub due to auth-required, but it has not AUTHed us! (Relay is buggy)");
555 to_probe.send(Command::Exit).await?;
556 break;
557 }
558
559 } else {
562 to_probe.send(Command::Exit).await?;
563 break;
564 }
565 }
566 }
567 RelayMessage::Notice(_) => {
568 to_probe.send(Command::Exit).await?;
569 break;
570 }
571 RelayMessage::Ok(id, is_ok, reason) => {
572 if let Some(authid) = authenticated {
573 if authid == id {
574 if is_ok {
575 to_probe
577 .send(Command::FetchEvents(
578 our_sub_id.clone(),
579 vec![filter.clone()],
580 ))
581 .await?;
582 } else {
583 eprintln!("AUTH failed: {}", reason);
584 to_probe.send(Command::Exit).await?;
585 break;
586 }
587 }
588 }
589 }
590 RelayMessage::Notify(_) => todo!(),
592 }
593 }
594
595 Ok(())
596}