diff --git a/src/nostr.rs b/src/nostr.rs index 5f84e9d..058dad2 100644 --- a/src/nostr.rs +++ b/src/nostr.rs @@ -23,7 +23,9 @@ impl WebSocketService { eat_whitespace(input, &mut inpos); verify_char(input, b'"', &mut inpos)?; if &input[inpos..inpos + 4] == b"REQ\"" { - self.req(msg, inpos + 4).await?; + self.req(msg, inpos + 4, false).await?; + } else if &input[inpos..inpos + 6] == b"COUNT\"" { + self.req(msg, inpos + 6, true).await?; } else if &input[inpos..inpos + 6] == b"EVENT\"" { self.event(msg, inpos + 6).await?; } else if &input[inpos..inpos + 6] == b"CLOSE\"" { @@ -39,7 +41,7 @@ impl WebSocketService { Ok(()) } - pub async fn req(&mut self, msg: &str, mut inpos: usize) -> Result<(), Error> { + pub async fn req(&mut self, msg: &str, mut inpos: usize, count: bool) -> Result<(), Error> { let input = msg.as_bytes(); // ["REQ", , json-filter, json-filter, ... ] @@ -75,7 +77,7 @@ impl WebSocketService { filters.push(filter.to_owned()); } - if let Err(e) = self.req_inner(&subid, filters).await { + if let Err(e) = self.req_inner(&subid, filters, count).await { let reply = match e.inner { ChorusError::TooManySubscriptions => { let max_subscriptions = GLOBALS.config.read().max_subscriptions; @@ -99,7 +101,12 @@ impl WebSocketService { } } - async fn req_inner(&mut self, subid: &String, filters: Vec) -> Result<(), Error> { + async fn req_inner( + &mut self, + subid: &String, + filters: Vec, + count: bool, + ) -> Result<(), Error> { let max_subscriptions = GLOBALS.config.read().max_subscriptions; if self.subscriptions.len() >= max_subscriptions { return Err(ChorusError::TooManySubscriptions.into()); @@ -120,7 +127,7 @@ impl WebSocketService { let reply = NostrReply::Closed( subid, NostrReplyPrefix::AuthRequired, - "DM kinds were included in the REQ".to_owned(), + "DM kinds were included in the filters".to_owned(), ); self.send(Message::text(reply.as_json())).await?; return Ok(()); @@ -162,24 +169,32 @@ impl WebSocketService { // dedup events.dedup(); - for event in events.drain(..) { - let reply = NostrReply::Event(subid, event); + if count { + let reply = NostrReply::Count(subid, events.len()); + self.send(Message::text(reply.as_json())).await?; + } else { + for event in events.drain(..) { + let reply = NostrReply::Event(subid, event); + self.send(Message::text(reply.as_json())).await?; + } + + // eose + let reply = NostrReply::Eose(subid); self.send(Message::text(reply.as_json())).await?; } - - // eose - let reply = NostrReply::Eose(subid); - self.send(Message::text(reply.as_json())).await?; } - // Store subscription - self.subscriptions.insert(subid.to_owned(), filters); + if !count { + // Store subscription + self.subscriptions.insert(subid.to_owned(), filters); - log::debug!(target: "Client", - "{}: new subscription \"{subid}\", {} total", - self.peer, - self.subscriptions.len() - ); + log::debug!( + target: "Client", + "{}: new subscription \"{subid}\", {} total", + self.peer, + self.subscriptions.len() + ); + } Ok(()) } diff --git a/src/reply.rs b/src/reply.rs index 5741063..075996d 100644 --- a/src/reply.rs +++ b/src/reply.rs @@ -36,19 +36,21 @@ pub enum NostrReply<'a> { Eose(&'a str), Closed(&'a str, NostrReplyPrefix, String), Notice(String), + Count(&'a str, usize), } impl NostrReply<'_> { pub fn as_json(&self) -> String { match self { - NostrReply::Auth(challenge) => format!(r#"["AUTH", "{challenge}"]"#), - NostrReply::Event(subid, event) => format!(r#"["EVENT", "{subid}", {}]"#, event), + NostrReply::Auth(challenge) => format!(r#"["AUTH","{challenge}"]"#), + NostrReply::Event(subid, event) => format!(r#"["EVENT","{subid}",{}]"#, event), NostrReply::Ok(id, ok, prefix, msg) => format!(r#"["OK","{id}",{ok},"{prefix}{msg}"]"#), NostrReply::Eose(subid) => format!(r#"["EOSE","{subid}"]"#), NostrReply::Closed(subid, prefix, msg) => { format!(r#"["CLOSED","{subid}","{prefix}{msg}"]"#) } NostrReply::Notice(msg) => format!(r#"["NOTICE","{msg}"]"#), + NostrReply::Count(subid, c) => format!(r#"["COUNT","{subid}",{{"count":{c}}}"#), } } } diff --git a/src/web/nip11.rs b/src/web/nip11.rs index e701709..547c3d5 100644 --- a/src/web/nip11.rs +++ b/src/web/nip11.rs @@ -27,20 +27,20 @@ pub async fn serve_nip11(peer: HashedPeer) -> Result String { let mut rid: String = String::with_capacity(255); - const SUPPORTED_NIPS: [u8; 8] = [ + const SUPPORTED_NIPS: [u8; 9] = [ 1, // nostr 4, // DMs 9, // Event Deletion 11, // relay information document 40, // Expiration Timestamp 42, // AUTH + 45, // Counting results 59, // GiftWrap 65, // Relay List Metadata ]; - const _UNSUPPORTED_NIPS: [u8; 6] = [ + const _UNSUPPORTED_NIPS: [u8; 5] = [ 26, // Delegated Event Signing 29, // Relay-based Groups - 45, // Counting results 50, // SEARCH 94, // File Metadata 96, // HTTP File Storage Integration