Support NIP-45 COUNT

This commit is contained in:
Mike Dilger 2024-12-11 07:29:14 +13:00
parent 969d67db6a
commit 17cd4edef5
No known key found for this signature in database
GPG Key ID: 47581A78D4329BA4
3 changed files with 40 additions and 23 deletions

View File

@ -23,7 +23,9 @@ impl WebSocketService {
eat_whitespace(input, &mut inpos); eat_whitespace(input, &mut inpos);
verify_char(input, b'"', &mut inpos)?; verify_char(input, b'"', &mut inpos)?;
if &input[inpos..inpos + 4] == b"REQ\"" { 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\"" { } else if &input[inpos..inpos + 6] == b"EVENT\"" {
self.event(msg, inpos + 6).await?; self.event(msg, inpos + 6).await?;
} else if &input[inpos..inpos + 6] == b"CLOSE\"" { } else if &input[inpos..inpos + 6] == b"CLOSE\"" {
@ -39,7 +41,7 @@ impl WebSocketService {
Ok(()) 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(); let input = msg.as_bytes();
// ["REQ", <subid>, json-filter, json-filter, ... ] // ["REQ", <subid>, json-filter, json-filter, ... ]
@ -75,7 +77,7 @@ impl WebSocketService {
filters.push(filter.to_owned()); 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 { let reply = match e.inner {
ChorusError::TooManySubscriptions => { ChorusError::TooManySubscriptions => {
let max_subscriptions = GLOBALS.config.read().max_subscriptions; let max_subscriptions = GLOBALS.config.read().max_subscriptions;
@ -99,7 +101,12 @@ impl WebSocketService {
} }
} }
async fn req_inner(&mut self, subid: &String, filters: Vec<OwnedFilter>) -> Result<(), Error> { async fn req_inner(
&mut self,
subid: &String,
filters: Vec<OwnedFilter>,
count: bool,
) -> Result<(), Error> {
let max_subscriptions = GLOBALS.config.read().max_subscriptions; let max_subscriptions = GLOBALS.config.read().max_subscriptions;
if self.subscriptions.len() >= max_subscriptions { if self.subscriptions.len() >= max_subscriptions {
return Err(ChorusError::TooManySubscriptions.into()); return Err(ChorusError::TooManySubscriptions.into());
@ -120,7 +127,7 @@ impl WebSocketService {
let reply = NostrReply::Closed( let reply = NostrReply::Closed(
subid, subid,
NostrReplyPrefix::AuthRequired, 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?; self.send(Message::text(reply.as_json())).await?;
return Ok(()); return Ok(());
@ -162,6 +169,10 @@ impl WebSocketService {
// dedup // dedup
events.dedup(); events.dedup();
if count {
let reply = NostrReply::Count(subid, events.len());
self.send(Message::text(reply.as_json())).await?;
} else {
for event in events.drain(..) { for event in events.drain(..) {
let reply = NostrReply::Event(subid, event); let reply = NostrReply::Event(subid, event);
self.send(Message::text(reply.as_json())).await?; self.send(Message::text(reply.as_json())).await?;
@ -171,15 +182,19 @@ impl WebSocketService {
let reply = NostrReply::Eose(subid); let reply = NostrReply::Eose(subid);
self.send(Message::text(reply.as_json())).await?; self.send(Message::text(reply.as_json())).await?;
} }
}
if !count {
// Store subscription // Store subscription
self.subscriptions.insert(subid.to_owned(), filters); self.subscriptions.insert(subid.to_owned(), filters);
log::debug!(target: "Client", log::debug!(
target: "Client",
"{}: new subscription \"{subid}\", {} total", "{}: new subscription \"{subid}\", {} total",
self.peer, self.peer,
self.subscriptions.len() self.subscriptions.len()
); );
}
Ok(()) Ok(())
} }

View File

@ -36,6 +36,7 @@ pub enum NostrReply<'a> {
Eose(&'a str), Eose(&'a str),
Closed(&'a str, NostrReplyPrefix, String), Closed(&'a str, NostrReplyPrefix, String),
Notice(String), Notice(String),
Count(&'a str, usize),
} }
impl NostrReply<'_> { impl NostrReply<'_> {
@ -49,6 +50,7 @@ impl NostrReply<'_> {
format!(r#"["CLOSED","{subid}","{prefix}{msg}"]"#) format!(r#"["CLOSED","{subid}","{prefix}{msg}"]"#)
} }
NostrReply::Notice(msg) => format!(r#"["NOTICE","{msg}"]"#), NostrReply::Notice(msg) => format!(r#"["NOTICE","{msg}"]"#),
NostrReply::Count(subid, c) => format!(r#"["COUNT","{subid}",{{"count":{c}}}"#),
} }
} }
} }

View File

@ -27,20 +27,20 @@ pub async fn serve_nip11(peer: HashedPeer) -> Result<Response<BoxBody<Bytes, Err
fn build_rid(config: &Config) -> String { fn build_rid(config: &Config) -> String {
let mut rid: String = String::with_capacity(255); let mut rid: String = String::with_capacity(255);
const SUPPORTED_NIPS: [u8; 8] = [ const SUPPORTED_NIPS: [u8; 9] = [
1, // nostr 1, // nostr
4, // DMs 4, // DMs
9, // Event Deletion 9, // Event Deletion
11, // relay information document 11, // relay information document
40, // Expiration Timestamp 40, // Expiration Timestamp
42, // AUTH 42, // AUTH
45, // Counting results
59, // GiftWrap 59, // GiftWrap
65, // Relay List Metadata 65, // Relay List Metadata
]; ];
const _UNSUPPORTED_NIPS: [u8; 6] = [ const _UNSUPPORTED_NIPS: [u8; 5] = [
26, // Delegated Event Signing 26, // Delegated Event Signing
29, // Relay-based Groups 29, // Relay-based Groups
45, // Counting results
50, // SEARCH 50, // SEARCH
94, // File Metadata 94, // File Metadata
96, // HTTP File Storage Integration 96, // HTTP File Storage Integration