gnostr_bins/
lib.rs

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;
7// pub //use nostr_types::RelayMessageV5;
8pub 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
16//pub use gnip44::*;
17pub 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
32//TODO
33/// get_relays_by_nip
34/// pub fn get_relays_by_nip(nip: &str) -> Result<String, &'static str>
35pub 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}
40/// get_relays <https://api.nostr.watch>
41/// pub fn get_relays() -> Result<String, &'static str>
42pub 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}
47/// get_relays_online <https://api.nostr.watch>
48/// pub fn get_relays_online() -> Result<String, &'static str>
49pub 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}
54/// get_relays_public <https://api.nostr.watch>
55/// pub fn get_relays_public() -> Result<String, &'static str>
56pub 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}
61/// get_relays_paid <https://api.nostr.watch>
62/// pub fn get_relays_paid() -> Result<String, &'static str>
63pub 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}
68/// get_relays_offline <https://api.nostr.watch>
69/// pub fn get_relays_offline() -> Result<String, &'static str>
70pub 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
76/// pub fn get_weeble() -> Result<String, &'static str>
77pub 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}
82/// pub fn get_wobble() -> Result<String, &'static str>
83pub 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}
88/// pub fn get_blockheight() -> Result<String, &'static str>
89pub 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}
94/// pub fn get_blockhash() -> Result<String, &'static str>
95pub 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
101/// pub fn hash_list()
102pub fn hash_list() {
103    let _ = ref_hash_list();
104}
105
106/// pub fn hash_list_padded()
107pub fn hash_list_padded() {
108    let _ = ref_hash_list_padded();
109}
110
111/// pub fn hash_list_w_commit_message()
112pub fn hash_list_w_commit_message() {
113    let _ = ref_hash_list_w_commit_message();
114}
115
116/// pub struct Config
117pub struct Config {
118    /// pub query: String
119    pub query: String,
120}
121use sha256::digest;
122use std::process;
123// impl Config {
124impl Config {
125    /// pub fn build(args: &\[String\]) -> Result\<Config, &'static str\>
126    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;
137/// pub fn run(config: Config) -> Result\<(), Box\<dyn Error\>\>
138pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
139    println!("{}", digest(config.query));
140    Ok(())
141}
142/// pub fn search\<'a\>(query: &str, contents: &'a str) -> Vec\<&'a str\> {
143pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
144    let mut results = Vec::new();
145    for line in contents.lines() {
146        // do something with line
147        println!("{}", line);
148        if line.contains(query) {
149            // do something with line
150            let val = digest(query);
151            println!("{}", val);
152            results.push(line);
153        }
154    }
155    results
156}
157
158/// pub fn post_event(url: &str, event: Event)
159pub 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}
164// /// use nostr_types::EventV2;
165// use nostr_types::EventV2;
166// /// pub fn post_event_v2(url: &str, event_v2: EventV2)
167// pub fn post_event_v2(url: &str, event_v2: EventV2) {
168//     let (host, uri) = url_to_host_and_uri(url);
169//     let wire = event_to_wire_v2(event_v2);
170//     post(host, uri, wire)
171// }
172/// use nostr_types::EventV3;
173use nostr_types::EventV3;
174/// pub fn post_event_v3(url: &str, event: EventV3)
175pub 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
181/// pub fn print_event(event: &Event)
182pub 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
192/// <https://docs.rs/gnostr-bins/latest/gnostr_bins/weeble/index.html>
193pub mod weeble;
194pub use weeble::weeble;
195
196/// <https://docs.rs/gnostr-bins/latest/gnostr_bins/wobble/index.html>
197pub mod wobble;
198pub use wobble::wobble;
199
200/// <https://docs.rs/gnostr-bins/latest/gnostr_bins/blockhash/index.html>
201pub mod blockhash;
202pub use blockhash::blockhash;
203
204/// <https://docs.rs/gnostr-bins/latest/gnostr_bins/blockheight/index.html>
205pub mod blockheight;
206pub use blockheight::blockheight;
207
208/// <https://docs.rs/gnostr-bins/latest/gnostr_bins/hash/index.html>
209pub mod hash;
210pub use hash::hash;
211
212/// REF: <https://api.nostr.watch>
213/// nostr.watch API Docs
214///
215/// Uptime absolutely not guaranteed
216///
217/// Endpoints
218///
219/// Supported Methods: GET
220///
221/// Online Relays: <https://api.nostr.watch/v1/online>
222/// Public Relays: <https://api.nostr.watch/v1/public>
223/// Pay to Relays: <https://api.nostr.watch/v1/paid>
224/// Offline Relays: <https://api.nostr.watch/v1/offline>
225/// Relays by supported NIP: <https://api.nostr.watch/v1/nip/X> Use NIP ids without leading zeros - for example: <https://api.nostr.watch/v1/nip/1>
226pub mod relays;
227
228/// pub fn fetch_by_filter(url: &str, filter: Filter) -> Vec\<Event\>
229pub 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
235/// pub fn fetch_by_id(url: &str, id: IdHex) -> Option\<Event\>
236pub 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; // use up the first immediate tick.
314
315        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                    // Display it
359                    Self::display(message.clone())?;
360
361                    // Take action
362                    match message {
363                        Message::Text(s) => {
364                            // Send back to main
365                            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        // Send close message before disconnecting
379        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                    //use nostr_types::RelayMessageV5;
421                    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                        // We have already authenticated. We will resubmit once we get the
560                        // OK message.
561                    } 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                            // Resubmit our request
576                            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            //use nostr_types::RelayMessageV5;
591            RelayMessage::Notify(_) => todo!(),
592        }
593    }
594
595    Ok(())
596}