From c3d9d07eec3b30d371f04242ee31dc702580b3a2 Mon Sep 17 00:00:00 2001 From: 8go <> Date: Wed, 14 Dec 2022 21:22:02 +0100 Subject: [PATCH] added subscription filter options --limit-number, --limit-days, --limit-hours, etc. --- .gitignore | 2 + Cargo.lock | 174 +++++++++++++++++++++++++-- Cargo.toml | 5 +- README.md | 42 +++++-- VERSION | 2 +- help.txt | 17 +++ src/main.rs | 335 ++++++++++++++++++++++++++++++++++++++-------------- 7 files changed, 471 insertions(+), 106 deletions(-) diff --git a/.gitignore b/.gitignore index 05d3689..6b3d107 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ todo.txt src.22*/* +src.20*/* +src.20* test.txt diff --git a/Cargo.lock b/Cargo.lock index eb0a673..5dcef16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.66" @@ -124,6 +133,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + [[package]] name = "chunked_transfer" version = "1.4.0" @@ -180,6 +204,22 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.5" @@ -208,6 +248,50 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.6" @@ -378,7 +462,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -490,9 +574,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", @@ -501,6 +585,30 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "idna" version = "0.3.0" @@ -543,9 +651,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "ec947b7a4ce12e3b87e353abae7ce124d025b6c7d6c5aea5cc0bcf92e9510ded" [[package]] name = "is-terminal" @@ -592,6 +700,15 @@ version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linux-raw-sys" version = "0.1.3" @@ -646,7 +763,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -677,9 +794,10 @@ dependencies = [ [[package]] name = "nostr-commander" -version = "0.0.7" +version = "0.0.8" dependencies = [ "anyhow", + "chrono", "clap", "directories", "json", @@ -722,6 +840,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.14.0" @@ -1008,6 +1145,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "sct" version = "0.7.0" @@ -1224,6 +1367,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1557,6 +1711,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 66d9dd3..013e52f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "nostr-commander" -version = "0.0.7" +version = "0.0.8" edition = "2021" description = "simple but convenient CLI-based Nostr client app for publishing,sending and subscribing" documentation = "https://docs.rs/nostr-commander" @@ -30,7 +30,8 @@ tracing-subscriber = "0.3" tracing = "0.1" directories = "4.0" update-informer = "0.5" -json = "0.12.4" +chrono = "0.4" +json = "0.12" [dev-dependencies] diff --git a/README.md b/README.md index ccfab8b..2287294 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,7 @@ you can copy and paste this config file to get going real fast. "secret_key_bech32": "nsec1yljk9us0e3whjnzysu6pqjhnw5wglkr6hvx4vj376fs0sfaxze6qvx5f5x", "public_key_bech32": "npub1af7ep6s5esrgtc2c7tlvd3v4jpna44qf6nhan8tek6h505nwrvgq38nwz6", "relays": [ - "wss://relay.nostr.info/", - "wss://nostr.ono.re/", - "wss://nostr.rocks/", - "wss://nostr-pub.wellorder.net/", - "wss://nostr.semisol.dev/", - "wss://nostr-relay.wlvs.space/" + "wss://nostr-pub.wellorder.net/" ], "metadata": { "name": "James Jones", @@ -84,22 +79,22 @@ you can copy and paste this config file to get going real fast. "contacts": [ { "pk": "887645fef0ce0c3c1218d2f5d8e6132a19304cdc57cd20281d082f38cfea0072", - "relay_url": "wss://nostr.openchain.fr/", + "relay_url": "wss://nostr-pub.wellorder.net/", "alias": "HackerNews" }, { "pk": "6b0d4c8d9dc59e110d380b0429a02891f1341a0fa2ba1b1cf83a3db4d47e3964", - "relay_url": "wss://nostr.openchain.fr/", + "relay_url": "wss://nostr-pub.wellorder.net/", "alias": "dergigi" }, { "pk": "3235036bd0957dfb27ccda02d452d7c763be40c91a1ac082ba6983b25238388c", - "relay_url": "wss://nostr.openchain.fr/", + "relay_url": "wss://nostr-pub.wellorder.net/", "alias": "vishalxl" }, { "pk": "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", - "relay_url": "wss://nostr.openchain.fr/", + "relay_url": "wss://nostr-pub.wellorder.net/", "alias": "jb55.com" } ], @@ -314,6 +309,33 @@ Options: Subscribe to one or more public keys. Specify each public key in form of 'npub1SomePublicKey'. Alternatively you can use the Hex form of the private key + --limit-number + Limit the number of messages to receive when subscribing. By default + there is no limit (0) [default: 0] + --limit-days + Limit the messages received to the last N days when subscribing. By + default there is no limit (0) [default: 0] + --limit-hours + Limit the messages received to the last N hours when subscribing. By + default there is no limit (0) [default: 0] + --limit-future-days + Limit the messages received to the next N days when subscribing. Stop + receiving N days in the future. By default there is no limit (0) [default: + 0] + --limit-future-hours + Limit the messages received to the last N hours when subscribing. + Stop receiving N hours in the future. By default there is no limit + (0) [default: 0] -h, --help Print help information (use `--help` for more detail) ``` + +# Other Related Projects + +- Look here for an [nostr awesome list](https://github.com/aljazceru/awesome-nostr). +- `nostr-commander` isn't quite what you wanted? + Check out [nostr_console](https://github.com/vishalxl/nostr_console). +- Not into `nostr` but into Matrix? + Check out [matrix-commander](https://github.com/8go/matrix-commander) + and [matrix-commander-rs](https://github.com/8go/matrix-commander-rs). +- Also [matrix-nostr-bridge](matrix-nostr-bridge). diff --git a/VERSION b/VERSION index 5a5831a..d169b2f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.7 +0.0.8 diff --git a/help.txt b/help.txt index fff26bd..7c70b99 100644 --- a/help.txt +++ b/help.txt @@ -181,5 +181,22 @@ Options: Subscribe to one or more public keys. Specify each public key in form of 'npub1SomePublicKey'. Alternatively you can use the Hex form of the private key + --limit-number + Limit the number of messages to receive when subscribing. By default + there is no limit (0) [default: 0] + --limit-days + Limit the messages received to the last N days when subscribing. By + default there is no limit (0) [default: 0] + --limit-hours + Limit the messages received to the last N hours when subscribing. By + default there is no limit (0) [default: 0] + --limit-future-days + Limit the messages received to the next N days when subscribing. Stop + receiving N days in the future. By default there is no limit (0) [default: + 0] + --limit-future-hours + Limit the messages received to the last N hours when subscribing. + Stop receiving N hours in the future. By default there is no limit + (0) [default: 0] -h, --help Print help information (use `--help` for more detail) diff --git a/src/main.rs b/src/main.rs index 72c2ffc..5eb3173 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ //! //! Usage: //! - run `nostr-commander-rs --help` -//! +//! //! For more information, see read the README.md //! //! file. @@ -22,7 +22,10 @@ use clap::{ColorChoice, Parser, ValueEnum}; use directories::ProjectDirs; // use mime::Mime; +use chrono::{Duration, Utc}; +// use json; use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; use std::env; use std::fmt::{self, Debug}; use std::fs::{self, File}; @@ -31,15 +34,15 @@ use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::str::FromStr; use thiserror::Error; -use tracing::{debug, enabled, error, info, warn, Level}; +use tracing::{debug, enabled, error, info, trace, warn, Level}; use tracing_subscriber; use update_informer::{registry, Check}; use url::Url; use nostr_sdk::{ + nostr::contact::Contact, nostr::event::kind::Kind, -nostr::event::kind::KindBase, - nostr::contact::Contact, + nostr::event::kind::KindBase, nostr::event::tag::TagKind, nostr::key::XOnlyPublicKey, nostr::key::{FromBech32, KeyError, Keys, ToBech32}, @@ -275,9 +278,9 @@ enum Output { /// is_ functions for the enum impl Output { - // pub fn is_text(&self) -> bool { - // self == &Self::Text - // } + pub fn is_text(&self) -> bool { + self == &Self::Text + } // pub fn is_json_spec(&self) -> bool { self == &Self::JsonSpec } } @@ -487,6 +490,8 @@ pub struct Args { #[arg(long, value_name = "RELAY_URI", num_args(0..), )] add_relay: Vec, + // todo remove-relay + // /// Specify one or multiple tag to attach to notes ot DMs. // #[arg(long)] // tag: Vec, @@ -609,7 +614,37 @@ pub struct Args { /// Alternatively you can use the Hex form of the private key. #[arg(long, value_name = "KEY", num_args(0..), )] subscribe_pubkey: Vec, - // todo: unsubscribe + // todo: unsubscribe_pubkey + // todo unsubscribe_author + // todo mpub1-to-hex + // todo hex-to-npub1 + // + /// Limit the number of messages to receive when subscribing. + /// By default there is no limit (0). + #[arg(long, value_name = "NUMBER", default_value_t = 0)] + limit_number: u16, + + /// Limit the messages received to the last N days when subscribing. + /// By default there is no limit (0). + #[arg(long, alias = "since-days", value_name = "DAYS", default_value_t = 0)] + limit_days: i64, + + /// Limit the messages received to the last N hours when subscribing. + /// By default there is no limit (0). + #[arg(long, alias = "since-hours", value_name = "HOURS", default_value_t = 0)] + limit_hours: i64, + + /// Limit the messages received to the next N days when subscribing. + /// Stop receiving N days in the future. + /// By default there is no limit (0). + #[arg(long, alias = "until-days", value_name = "DAYS", default_value_t = 0)] + limit_future_days: i64, + + /// Limit the messages received to the last N hours when subscribing. + /// Stop receiving N hours in the future. + /// By default there is no limit (0). + #[arg(long, alias = "until-hours", value_name = "HOURS", default_value_t = 0)] + limit_future_hours: i64, } impl Default for Args { @@ -628,6 +663,7 @@ impl Args { log_level: LogLevel::None, verbose: 0u8, // plain: false, + // credentials file path credentials: get_credentials_default_path(), create_user: false, delete_user: false, @@ -658,6 +694,11 @@ impl Args { relay: Vec::new(), subscribe_author: Vec::new(), subscribe_pubkey: Vec::new(), + limit_number: 0, + limit_days: 0, + limit_hours: 0, + limit_future_days: 0, + limit_future_hours: 0, } } } @@ -1638,38 +1679,55 @@ pub(crate) async fn cli_subscribe_pubkey(client: &mut Client, ap: &mut Args) -> } /// Utility function to print JSON object as JSON or as plain text -pub(crate) fn print_json(json_data: &json::JsonValue, output: Output) { - debug!("{:?}", json_data); +/// depth: depth in nesting, on first call use 0. +// see https://github.com/serde-rs/json +pub(crate) fn print_json(jsonv: &Value, output: Output, depth: u32, separator: &str) { + trace!("{:?}", jsonv); match output { Output::Text => { - let mut first = true; - for (key, val) in json_data.entries() { - if first { - first = false; - } else { + if depth != 0 { + print!(" "); + } + if jsonv.is_object() { + // if it is an object, check recursively + for (key, val) in jsonv.as_object().unwrap() { + print!("{}:", key); + print_json(val, output, depth + 1, separator); print!(" "); } - print!("{}:", key); - if val.is_object() { - // if it is an object, check recursively - print_json(val, output); - } else if val.is_boolean() { - print!(" {}", val); - } else if val.is_null() { - print!(" "); // print nothing - } else if val.is_string() { - print!(" {}", val); - } else if val.is_number() { - print!(" {}", val); - } else if val.is_array() { - print!(" [{}]", val); + } else if jsonv.is_boolean() { + print!("{}", jsonv); + } else if jsonv.is_null() { + print!(""); // print nothing + } else if jsonv.is_string() { + print!("{}", jsonv); + } else if jsonv.is_number() { + print!("{}", jsonv); + } else if jsonv.is_array() { + print!("[ "); + print!("{}", separator); + let mut i = 0; + while i < jsonv.as_array().unwrap().len() { + if i > 0 { + print!(", "); + } + print_json(&jsonv[i], output, depth + 1, separator); + i += 1; + println!(); } + print!("{}", separator); + print!(" ]"); + } else { + debug!("not implemented type in print_json()"); + print!("{}", jsonv.to_string(),); + } + if depth == 0 { + println!(); } - println!(); } Output::JsonSpec => (), _ => { - println!("{}", json_data.dump(),); + println!("{}", jsonv.to_string(),); } } } @@ -1677,11 +1735,13 @@ pub(crate) fn print_json(json_data: &json::JsonValue, output: Output) { /// Handle the --whoami CLI argument pub(crate) fn cli_whoami(ap: &Args) -> Result<(), Error> { print_json( - &json::object!( - name: ap.creds.metadata.name.clone(), - display_name: ap.creds.metadata.display_name.clone(), - ), + &json!({ + "name": ap.creds.metadata.name.clone(), + "display_name": ap.creds.metadata.display_name.clone(), + }), ap.output, + 0, + "", ); Ok(()) } @@ -1808,7 +1868,7 @@ async fn main() -> Result<(), Error> { ); } } - + // todo clean up code to separate better local action from client/remote action // Add relays, if --create-user the relays have already been added if !ap.add_relay.is_empty() && !ap.create_user { match crate::cli_add_relay(&mut client, &mut ap) { @@ -1821,18 +1881,31 @@ async fn main() -> Result<(), Error> { } } - // Connect to relays, WAIT for connection, and keep connection alive - // match client.connect().await { - match client.connect_and_wait().await { - Ok(()) => { - info!("connect successful."); - } - Err(ref e) => { - error!( - "connect failed. Could not connect to relays. Reported error is: {:?}", - e - ); - return Err(Error::CannotConnectToRelays); + if ap.listen + || !ap.publish_pow.is_empty() + || !ap.publish.is_empty() + || !ap.dm.is_empty() + || !ap.subscribe_author.is_empty() + || !ap.subscribe_pubkey.is_empty() + { + // todo avoid connect_...() call if not relay action is needed and everything can be done locally. + // todo avoid connect...() if no client is needed. + // + // Connect to relays, WAIT for connection, and keep connection alive + // todo only use the wait version if there is no -l and there is some publish + // also do a wait on create-user ? + // match client.connect().await { + match client.connect_and_wait().await { + Ok(()) => { + info!("connect successful."); + } + Err(ref e) => { + error!( + "connect failed. Could not connect to relays. Reported error is: {:?}", + e + ); + return Err(Error::CannotConnectToRelays); + } } } @@ -1889,7 +1962,13 @@ async fn main() -> Result<(), Error> { } } if ap.show_contacts { - println!("Contacts: {:?}", ap.creds.contacts); + if ap.output.is_text() { + for c in &ap.creds.contacts { + print_json(&json!(c), ap.output, 0, ""); + } + } else { + print_json(&json!({"contacts": ap.creds.contacts}), ap.output, 0, ""); + } } // ap.creds.save(get_credentials_actual_path(&ap))?; // do it later @@ -1931,67 +2010,104 @@ async fn main() -> Result<(), Error> { if !ap.subscribe_author.is_empty() { match crate::cli_subscribe_author(&mut client, &mut ap).await { Ok(()) => { - info!("subscribe_author successful."); + debug!("subscribe_author successful. Subscriptions synchronized with credentials file."); } Err(ref e) => { error!("subscribe_author failed. Reported error is: {:?}", e); } } } - match client - .subscribe(vec![ - SubscriptionFilter::new().authors(ap.creds.subscribed_authors.clone()) - ]) - .await - { - Ok(()) => { - info!("subscribe to authors successful."); + if !ap.creds.subscribed_authors.is_empty() { + let mut asf: SubscriptionFilter; + asf = SubscriptionFilter::new().authors(ap.creds.subscribed_authors.clone()); + if ap.limit_number != 0 { + asf = asf.limit(ap.limit_number); } - Err(ref e) => { - error!("subscribe to authors failed. Reported error is: {:?}", e); + if ap.limit_days != 0 { + asf = asf.since((Utc::now() - Duration::days(ap.limit_days)).timestamp() as u64); + } + if ap.limit_hours != 0 { + asf = asf.since((Utc::now() - Duration::hours(ap.limit_hours)).timestamp() as u64); + } + if ap.limit_future_days != 0 { + asf = asf.until((Utc::now() - Duration::days(ap.limit_future_days)).timestamp() as u64); + } + if ap.limit_future_hours != 0 { + asf = + asf.until((Utc::now() - Duration::hours(ap.limit_future_hours)).timestamp() as u64); + } + match client.subscribe(vec![asf]).await { + Ok(()) => { + info!("subscribe to authors successful."); + } + Err(ref e) => { + error!("subscribe to authors failed. Reported error is: {:?}", e); + } } } // Subscribe keys if !ap.subscribe_pubkey.is_empty() { match crate::cli_subscribe_pubkey(&mut client, &mut ap).await { Ok(()) => { - info!("subscribe_pubkey successful."); + debug!("subscribe_pubkey successful. Subscriptions synchronized with credentials file."); } Err(ref e) => { error!("subscribe_pubkey failed. Reported error is: {:?}", e); } } } - match client - .subscribe(vec![ - SubscriptionFilter::new().pubkeys(ap.creds.subscribed_pubkeys.clone()) - ]) - .await - { - Ok(()) => { - info!("subscribe to pubkeys successful."); + if !ap.creds.subscribed_pubkeys.is_empty() { + let mut ksf: SubscriptionFilter; + ksf = SubscriptionFilter::new().pubkeys(ap.creds.subscribed_pubkeys.clone()); + if ap.limit_number != 0 { + ksf = ksf.limit(ap.limit_number); } - Err(ref e) => { - error!("subscribe to pubkeys failed. Reported error is: {:?}", e); + if ap.limit_days != 0 { + ksf = ksf.since((Utc::now() - Duration::days(ap.limit_days)).timestamp() as u64); + } + if ap.limit_hours != 0 { + ksf = ksf.since((Utc::now() - Duration::hours(ap.limit_hours)).timestamp() as u64); + } + if ap.limit_future_days != 0 { + ksf = ksf.until((Utc::now() - Duration::days(ap.limit_future_days)).timestamp() as u64); + } + if ap.limit_future_hours != 0 { + ksf = + ksf.until((Utc::now() - Duration::hours(ap.limit_future_hours)).timestamp() as u64); + } + match client.subscribe(vec![ksf]).await { + Ok(()) => { + info!("subscribe to pubkeys successful."); + } + Err(ref e) => { + error!("subscribe to pubkeys failed. Reported error is: {:?}", e); + } } } ap.creds.save(get_credentials_actual_path(&ap))?; // notices will be published even if we do not go into handle_notification event loop - // Do not automatically listen when subscriptions exist, only listen to subscriptions if --listen is set. + // Design choice: Do not automatically listen when subscriptions exist, only listen to subscriptions if --listen is set. if ap.listen // || !ap.creds.subscribed_authors.is_empty() // || !ap.creds.subscribed_pubkeys.is_empty() { let num = ap.publish.len() + ap.publish_pow.len(); - info!( - "You should be receiving {:?} 'OK' messages with event ids, one for each notice that has been relayed.", - num - ); + if num == 1 { + info!( + "You should be receiving {:?} 'OK' message with event id for the notice once it has been relayed.", + num + ); + } else if num > 1 { + info!( + "You should be receiving {:?} 'OK' messages with event ids, one for each notice that has been relayed.", + num + ); + } // Handle notifications match client .handle_notifications(|notification| { - // debug!("Notification: {:?}", notification); + trace!("Notification: {:?}", notification); match notification { ReceivedEvent(ev) => { debug!("Event-Event: content {:?}, kind {:?}", ev.content, ev.kind); @@ -2003,7 +2119,17 @@ async fn main() -> Result<(), Error> { // Notification: ReceivedMessage(Ok { event_id: 123, status: true, message: "" }) // confirmation of notice having been relayed info!("Message-OK: Notice or DM was relayed. Event id is {:?}. Status is {:?} and message is {:?}. You can investigate this event by looking it up on https://nostr.com/e/{}", event_id, status, message, event_id.to_string()); - println!("Message-OK: Notice or DM was relayed. Event id is {:?}. Status is {:?} and message is {:?}. You can investigate this event by looking it up on https://nostr.com/e/{}", event_id, status, message, event_id.to_string()); + print_json( + &json!({"event_type": "RelayMessage::Ok", + "event_type_meaning": "Notice or DM was relayed successfully.", + "event_id": event_id, + "status": status, + "message": message, + "event_url": "https://nostr.com/e/".to_string() + &event_id.to_string(), + "event_url_meaning": "You can investigate this event by looking up the event URL.", + }) , + ap.output,0,"" + ); }, RelayMessage::Notice { message } => { debug!("Message-Notice: {:?}", message); @@ -2017,7 +2143,7 @@ async fn main() -> Result<(), Error> { Ok(TagKind::P) => { match t.content() { Some(c) => { - debug!("tag: {:?}", get_contact_alias_or_keystr_by_keystr(&ap, c)); + trace!("tag: {:?}", get_contact_alias_or_keystr_by_keystr(&ap, c)); match get_contact_alias_by_keystr(&ap, c) { Some(a) => { if !first { tags += ", "; }; @@ -2035,26 +2161,63 @@ async fn main() -> Result<(), Error> { Err(_) => () } } - info!("Message-Event: content {:?}, kind {:?}, from pubkey {:?}, with tags {:?}", event.content, event.kind, get_contact_alias_or_keystr_by_key(&ap, event.pubkey), event.tags); + trace!("Message-Event: content {:?}, kind {:?}, from pubkey {:?}, with tags {:?}", event.content, event.kind, get_contact_alias_or_keystr_by_key(&ap, event.pubkey), event.tags); let mut key_author = "key"; if is_subscribed_author(&ap, &event.pubkey) { key_author = "author"; tags = get_contact_alias_or_keystr_by_key(&ap, event.pubkey); }; match event.kind { - Kind::Base(KindBase::ContactList) => (), - Kind::Base(KindBase::Reaction) => (), + Kind::Base(KindBase::ContactList) => { + debug!("Received Message-Event ContactList"); + }, + Kind::Base(KindBase::Reaction) => { + debug!("Received Message-Event Reaction: content {:?}", event.content); + }, Kind::Base(KindBase::TextNote) => { - println!("Subscription by {} ({}): content {:?}, kind {:?}, from pubkey {:?}", key_author, tags, event.content, event.kind, get_contact_alias_or_keystr_by_key(&ap, event.pubkey)); + info!("Subscription by {} ({}): content {:?}, kind {:?}, from pubkey {:?}", key_author, tags, event.content, event.kind, get_contact_alias_or_keystr_by_key(&ap, event.pubkey)); + print_json( + &json!({ + "event_type": "RelayMessage::Event", + "event_type_meaning": "Message was received because of subscription.", + "subscribed_by": key_author, + "author": get_contact_alias_or_keystr_by_key(&ap, event.pubkey), + "content": event.content, + // "kind": event.kind, // writes integer like '1' + "kind": format!("{:?}",event.kind), // writes text like "Base(TextNote)" + "from_alias": get_contact_alias_or_keystr_by_key(&ap, event.pubkey), + "from_pubkey": event.pubkey, + "tags": tags + }) , + ap.output,0,"" + ); }, Kind::Base(KindBase::ChannelMessage) => { - println!("Subscription by {} ({}): content {:?}, kind {:?}, from pubkey {:?}", key_author, tags, event.content, event.kind, get_contact_alias_or_keystr_by_key(&ap, event.pubkey)); + warn!("Subscription by {} ({}): content {:?}, kind {:?}, from pubkey {:?}", key_author, tags, event.content, event.kind, get_contact_alias_or_keystr_by_key(&ap, event.pubkey)); + print_json( + &json!({ + "event_type": "RelayMessage::Event", + "event_type_meaning": "Message was received because of subscription.", + "subscribed_by": key_author, + "author": get_contact_alias_or_keystr_by_key(&ap, event.pubkey), + "content": event.content, + "kind": format!("{:?}",event.kind), + "from_alias": get_contact_alias_or_keystr_by_key(&ap, event.pubkey), + "from_pubkey": event.pubkey, + "tags": tags + }) , + ap.output,0,"" + ); }, _ => () } }, - RelayMessage::Empty => (), - RelayMessage::EndOfStoredEvents {subscription_id} => (), + RelayMessage::Empty => { + debug!("Received Message-Event Empty"); + }, + RelayMessage::EndOfStoredEvents {subscription_id} => { + debug!("Received Message-Event EndOfStoredEvents"); + }, } } }