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);
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", <subid>, 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<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;
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(())
}

View File

@ -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}}}"#),
}
}
}

View File

@ -27,20 +27,20 @@ pub async fn serve_nip11(peer: HashedPeer) -> Result<Response<BoxBody<Bytes, Err
fn build_rid(config: &Config) -> 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