diff --git a/nostr-java-api/pom.xml b/nostr-java-api/pom.xml
index 3c3ab98db..c324dde9f 100644
--- a/nostr-java-api/pom.xml
+++ b/nostr-java-api/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-api
jar
@@ -45,9 +45,9 @@
${project.version}
- ${project.groupId}
- nostr-java-command-provider
- ${project.version}
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
diff --git a/nostr-java-api/src/main/java/module-info.java b/nostr-java-api/src/main/java/module-info.java
index e7b5625f1..3290a781e 100644
--- a/nostr-java-api/src/main/java/module-info.java
+++ b/nostr-java-api/src/main/java/module-info.java
@@ -5,16 +5,16 @@
requires nostr.id;
requires nostr.client;
requires nostr.context;
- requires nostr.context.impl;
requires nostr.encryption;
requires nostr.encryption.nip04dm;
requires nostr.encryption.nip44dm;
-
+
requires com.fasterxml.jackson.databind;
-
+
requires lombok;
requires java.logging;
requires nostr.crypto;
+ requires org.apache.commons.lang3;
exports nostr.api;
}
diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java
index 53d91db10..af3310462 100644
--- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java
+++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java
@@ -10,15 +10,16 @@
import lombok.Setter;
import nostr.api.factory.impl.GenericEventFactory;
import nostr.base.PublicKey;
+import nostr.event.BaseMessage;
import nostr.event.BaseTag;
import nostr.event.impl.GenericEvent;
import nostr.event.json.codec.BaseMessageDecoder;
import nostr.id.Identity;
+import org.apache.commons.lang3.stream.Streams.FailableStream;
import java.util.List;
import java.util.Map;
-
-import nostr.event.BaseMessage;
+import java.util.Objects;
/**
* @author guilhermegps
@@ -46,14 +47,14 @@ public U send() {
return this.send(getRelays());
}
- @SuppressWarnings("unchecked")
public U send(Map relays) {
- List messages = super.send(this.event, relays);
- BaseMessageDecoder decoder = new BaseMessageDecoder();
+ List messages = super.sendEvent(this.event, relays);
+ BaseMessageDecoder decoder = new BaseMessageDecoder<>();
- return messages.stream()
+ return new FailableStream<>(messages.stream())
.map(msg -> (U) decoder.decode(msg))
- .filter(msg -> msg != null)
+ .filter(Objects::nonNull)
+ .stream()
.findFirst()
.orElseThrow(() -> new RuntimeException("No message received"));
}
diff --git a/nostr-java-api/src/main/java/nostr/api/NIP01.java b/nostr-java-api/src/main/java/nostr/api/NIP01.java
index c91ccf304..16591fe8f 100644
--- a/nostr-java-api/src/main/java/nostr/api/NIP01.java
+++ b/nostr-java-api/src/main/java/nostr/api/NIP01.java
@@ -19,17 +19,14 @@
import nostr.api.factory.impl.NIP01Impl.ReplaceableEventFactory;
import nostr.api.factory.impl.NIP01Impl.ReqMessageFactory;
import nostr.api.factory.impl.NIP01Impl.TextNoteEventFactory;
-import nostr.base.GenericTagQuery;
import nostr.base.IEvent;
import nostr.base.PublicKey;
import nostr.base.Relay;
import nostr.base.UserProfile;
import nostr.event.BaseTag;
-import nostr.event.Kind;
import nostr.event.Marker;
import nostr.event.NIP01Event;
-import nostr.event.impl.Filters;
-import nostr.event.impl.GenericEvent;
+import nostr.event.filter.Filters;
import nostr.event.message.CloseMessage;
import nostr.event.message.EoseMessage;
import nostr.event.message.EventMessage;
@@ -42,7 +39,6 @@
import nostr.id.Identity;
import java.util.List;
-import java.util.Map;
/**
*
@@ -98,7 +94,7 @@ public NIP01 createMetadataEvent(@NonNull UserProfile profile) {
/**
* Create a replaceable event
- *
+ *
* @param kind the kind (10000 <= kind < 20000 || kind == 0 || kind == 3)
* @param content the content
*/
@@ -111,7 +107,7 @@ public NIP01 createReplaceableEvent(@NonNull Integer kind, String content) {
/**
* Create a replaceable event
- *
+ *
* @param tags the note's tags
* @param kind the kind (10000 <= kind < 20000 || kind == 0 || kind == 3)
* @param content the note's content
@@ -125,7 +121,7 @@ public NIP01 createReplaceableEvent(@NonNull List tags, @NonNull Int
/**
* Create an ephemeral event
- *
+ *
* @param kind the kind (20000 <= n < 30000)
* @param content the note's content
*/
@@ -190,48 +186,6 @@ public static PubKeyTag createPubKeyTag(@NonNull PublicKey publicKey, String mai
return result;
}
- /**
- * Create a NIP01 filters object (all parameters are optional)
- *
- * @param events a list of event
- * @param authors a list of pubkeys or prefixes, the pubkey of an event
- * must
- * be one of these
- * @param kinds a list of a kind numbers
- * @param referencedEvents a list of event ids that are referenced in an "e"
- * tag
- * @param referencePubKeys a list of pubkeys that are referenced in a "p"
- * tag
- * @param since an integer unix timestamp in seconds, events must be
- * newer
- * than this to pass
- * @param until an integer unix timestamp in seconds, events must be
- * older
- * than this to pass
- * @param limit maximum number of events to be returned in the
- * initial query
- * @param genericTagQuery a generic tag query
- * @return a filters object
- */
- @Deprecated(forRemoval = true)
- public static Filters createFilters(List events, List authors, List kinds,
- List referencedEvents, List referencePubKeys, Long since, Long until,
- Integer limit, GenericTagQuery genericTagQuery) {
- return Filters.builder()
- .authors(authors)
- .events(events)
- .genericTagQuery(
- Map.of(
- genericTagQuery.getTagName(),
- genericTagQuery.getValue()))
- .kinds(kinds).limit(limit)
- .referencePubKeys(referencePubKeys)
- .referencedEvents(referencedEvents)
- .since(since)
- .until(until)
- .build();
- }
-
/**
* Create an event message to send events requested by clients
*
diff --git a/nostr-java-api/src/main/java/nostr/api/NIP52.java b/nostr-java-api/src/main/java/nostr/api/NIP52.java
index 8189316a4..fc2e6e0e5 100644
--- a/nostr-java-api/src/main/java/nostr/api/NIP52.java
+++ b/nostr-java-api/src/main/java/nostr/api/NIP52.java
@@ -1,21 +1,28 @@
package nostr.api;
import lombok.NonNull;
+import nostr.api.factory.impl.NIP52Impl.CalendarRsvpEventFactory;
import nostr.api.factory.impl.NIP52Impl.CalendarTimeBasedEventFactory;
import nostr.event.BaseTag;
import nostr.event.NIP52Event;
import nostr.event.impl.CalendarContent;
+import nostr.event.impl.CalendarRsvpContent;
import nostr.id.Identity;
import java.util.List;
public class NIP52 extends EventNostr {
- public NIP52(@NonNull Identity sender) {
- setSender(sender);
- }
+ public NIP52(@NonNull Identity sender) {
+ setSender(sender);
+ }
- public NIP52 createCalendarTimeBasedEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarContent calendarContent) {
- setEvent((T) new CalendarTimeBasedEventFactory(getSender(), baseTags, content, calendarContent).create());
- return this;
- }
+ public NIP52 createCalendarTimeBasedEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarContent calendarContent) {
+ setEvent((T) new CalendarTimeBasedEventFactory(getSender(), baseTags, content, calendarContent).create());
+ return this;
+ }
+
+ public NIP52 createCalendarRsvpEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) {
+ setEvent((T) new CalendarRsvpEventFactory(getSender(), baseTags, content, calendarRsvpContent).create());
+ return this;
+ }
}
diff --git a/nostr-java-api/src/main/java/nostr/api/Nostr.java b/nostr-java-api/src/main/java/nostr/api/Nostr.java
deleted file mode 100644
index aaf4b488d..000000000
--- a/nostr-java-api/src/main/java/nostr/api/Nostr.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
- * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
- */
-package nostr.api;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import nostr.base.GenericTagQuery;
-import nostr.base.IElement;
-import nostr.base.IEvent;
-import nostr.base.ISignable;
-import nostr.base.Relay;
-import nostr.client.Client;
-import nostr.context.RequestContext;
-import nostr.context.impl.DefaultRequestContext;
-import nostr.crypto.schnorr.Schnorr;
-import nostr.event.BaseEvent;
-import nostr.event.BaseMessage;
-import nostr.event.BaseTag;
-import nostr.event.impl.Filters;
-import nostr.event.impl.GenericEvent;
-import nostr.event.json.codec.BaseEventEncoder;
-import nostr.event.json.codec.BaseMessageDecoder;
-import nostr.event.json.codec.BaseTagDecoder;
-import nostr.event.json.codec.BaseTagEncoder;
-import nostr.event.json.codec.FiltersDecoder;
-import nostr.event.json.codec.FiltersListEncoder;
-import nostr.event.json.codec.GenericEventDecoder;
-import nostr.event.json.codec.GenericTagQueryEncoder;
-import nostr.event.message.EventMessage;
-import nostr.event.message.ReqMessage;
-import nostr.id.Identity;
-import nostr.util.NostrUtil;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeoutException;
-
-/**
- * @author eric
- */
-@NoArgsConstructor
-public class Nostr implements NostrIF {
-
- private static Nostr INSTANCE;
-
- private Client client;
- @Getter
- private Identity sender;
-
- @Getter
- private Map relays;
-
- public static NostrIF getInstance() {
- return (INSTANCE == null) ? new Nostr() : INSTANCE;
- }
-
- public static NostrIF getInstance(@NonNull Identity sender) {
- return (INSTANCE == null) ? new Nostr(sender) : INSTANCE;
- }
-
- public Nostr(@NonNull Identity sender) {
- this.sender = sender;
- }
-
- @Override
- public NostrIF setSender(@NonNull Identity sender) {
- this.sender = sender;
-
- return this;
- }
-
- @Override
- public NostrIF setRelays(@NonNull Map relays) {
- this.relays = relays;
-
- return this;
- }
-
- @Override
- public void close() {
- if (client == null) {
- throw new IllegalStateException("Client is not initialized");
- }
- try {
- this.client.disconnect();
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public List send(@NonNull IEvent event) {
- return send(event, getRelays());
- }
-
- @Override
- public List send(@NonNull IEvent event, Map relays) {
- var context = new DefaultRequestContext();
- context.setPrivateKey(getSender().getPrivateKey().getRawData());
- context.setRelays(relays);
-
- return send(new EventMessage(event), context);
- }
-
-
- @Override
- public List send(@NonNull Filters filters, @NonNull String subscriptionId) {
- return send(filters, subscriptionId, getRelays());
- }
-
- @Override
- public List send(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) {
- List filtersList = new ArrayList<>();
- filtersList.add(filters);
-
- return send(filtersList, subscriptionId, relays);
- }
-
- @Override
- public List send(@NonNull List filtersList, @NonNull String subscriptionId) {
- return send(filtersList, subscriptionId, getRelays());
- }
-
- @Override
- public List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) {
-
- var context = new DefaultRequestContext();
- context.setRelays(relays);
- context.setPrivateKey(getSender().getPrivateKey().getRawData());
- var message = new ReqMessage(subscriptionId, filtersList);
-
- return send(message, context);
- }
-
- @Override
- public List send(@NonNull BaseMessage message, @NonNull RequestContext context) {
- if (context instanceof DefaultRequestContext) {
- try {
- Client.getInstance().connect(context).send(message);
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
- }
- }
- return List.of();
- }
-
- /**
- * @param signable
- */
- @Override
- public NostrIF sign(@NonNull Identity identity, @NonNull ISignable signable) {
- identity.sign(signable);
-
- return this;
- }
-
- @Override
- public boolean verify(@NonNull GenericEvent event) {
- if (!event.isSigned()) {
- throw new IllegalStateException("The event is not signed");
- }
-
- var signature = event.getSignature();
-
- try {
- var message = NostrUtil.sha256(event.get_serializedEvent());
- return Schnorr.verify(message, event.getPubKey().getRawData(), signature.getRawData());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static Map toMapRelays(List relayList) {
- Map relays = new HashMap<>();
- relayList.forEach(r -> relays.put(r.getName(), r.getUri()));
- return relays;
- }
-
- public static class Json {
-
- // Events
-
- /**
- * @param event
- */
- public static String encode(@NonNull BaseEvent event) {
- return Nostr.Json.encode(event, null);
- }
-
- /**
- * @param event
- * @param relay
- * @return
- */
- public static String encode(@NonNull BaseEvent event, Relay relay) {
- final var enc = new BaseEventEncoder(event);
- return enc.encode();
- }
-
- /**
- * @param json
- */
- public static GenericEvent decodeEvent(@NonNull String json) {
- return new GenericEventDecoder<>().decode(json);
- }
-
- // Messages
-
- /**
- * @param message
- * @param relay
- */
- public static String encode(@NonNull BaseMessage message, Relay relay) throws JsonProcessingException {
- return message.encode();
- }
-
- /**
- * @param message
- */
- public static String encode(@NonNull BaseMessage message) throws JsonProcessingException {
- return Nostr.Json.encode(message, null);
- }
-
- /**
- * @param json
- */
- public static BaseMessage decodeMessage(@NonNull String json) {
- return new BaseMessageDecoder().decode(json);
- }
-
- // Tags
-
- /**
- * @param tag
- * @param relay
- */
- public static String encode(@NonNull BaseTag tag, Relay relay) {
- final var enc = new BaseTagEncoder(tag, relay);
- return enc.encode();
- }
-
- /**
- * @param tag
- */
- public static String encode(@NonNull BaseTag tag) {
- return Nostr.Json.encode(tag, null);
- }
-
- /**
- * @param json
- */
- public static BaseTag decodeTag(@NonNull String json) {
- return new BaseTagDecoder<>().decode(json);
- }
-
- // Filters
-
- /**
- * @param filtersList
- * @param relay
- */
- public static String encode(@NonNull List filtersList, Relay relay) {
- final var enc = new FiltersListEncoder(filtersList);
- return enc.encode();
- }
-
- /**
- * @param filtersList
- */
- public static String encode(@NonNull List filtersList) {
- return Nostr.Json.encode(filtersList, null);
- }
-
- /**
- * @param json
- */
- public static Filters decodeFilters(@NonNull String json) {
- return new FiltersDecoder<>().decode(json);
- }
-
- // Generic Tag Queries
-
- /**
- * @param gtq
- * @param relay
- */
- public static String encode(@NonNull GenericTagQuery gtq, Relay relay) {
- final var enc = new GenericTagQueryEncoder(gtq, relay);
- return enc.encode();
- }
-
- /**
- * @param gtq
- */
- public static String encode(@NonNull GenericTagQuery gtq) {
- return Nostr.Json.encode(gtq, null);
- }
-
- /**
- * @param json
- * @param clazz
- */
- public static IElement decode(@NonNull String json, @NonNull Class clazz) {
- switch (clazz.getName()) {
- case "nostr.event.BaseEvent.class" -> {
- return decodeEvent(json);
- }
- case "nostr.event.BaseMessage.class" -> {
- return decodeMessage(json);
- }
- case "nostr.event.BaseTag.class" -> {
- return decodeTag(json);
- }
- default -> throw new AssertionError();
- }
- }
-
- public static Filters decodeFilters(@NonNull String json, @NonNull Class clazz) {
- switch (clazz.getName()) {
- case "nostr.event.Filters.class" -> {
- return decodeFilters(json);
- }
- default -> throw new AssertionError();
- }
- }
-
- }
-}
diff --git a/nostr-java-api/src/main/java/nostr/api/NostrIF.java b/nostr-java-api/src/main/java/nostr/api/NostrIF.java
index 0d920f7c9..aecd93a6a 100644
--- a/nostr-java-api/src/main/java/nostr/api/NostrIF.java
+++ b/nostr-java-api/src/main/java/nostr/api/NostrIF.java
@@ -5,7 +5,7 @@
import nostr.base.ISignable;
import nostr.context.RequestContext;
import nostr.event.BaseMessage;
-import nostr.event.impl.Filters;
+import nostr.event.filter.Filters;
import nostr.event.impl.GenericEvent;
import nostr.id.Identity;
@@ -16,13 +16,13 @@
public interface NostrIF {
NostrIF setSender(@NonNull Identity sender);
NostrIF setRelays(@NonNull Map relays);
- List send(@NonNull IEvent event);
- List send(@NonNull IEvent event, Map relays);
- List send(@NonNull Filters filters, @NonNull String subscriptionId);
- List send(@NonNull Filters filters, @NonNull String subscriptionId, Map relays);
- List send(@NonNull List filtersList, @NonNull String subscriptionId);
- List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays);
- List send(@NonNull BaseMessage message, @NonNull RequestContext context);
+ List sendEvent(@NonNull IEvent event);
+ List sendEvent(@NonNull IEvent event, Map relays);
+ List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId);
+ List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId, Map relays);
+ List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId);
+ List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId, Map relays);
+ List sendRequest(@NonNull BaseMessage message, @NonNull RequestContext context);
NostrIF sign(@NonNull Identity identity, @NonNull ISignable signable);
boolean verify(@NonNull GenericEvent event);
Identity getSender();
diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java
index 5389e644d..a2955c1d3 100644
--- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java
+++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java
@@ -1,45 +1,36 @@
package nostr.api;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
-import lombok.SneakyThrows;
import nostr.base.IEvent;
import nostr.base.ISignable;
-import nostr.client.springwebsocket.SpringWebSocketClient;
import nostr.context.RequestContext;
import nostr.crypto.schnorr.Schnorr;
import nostr.event.BaseMessage;
-import nostr.event.impl.Filters;
+import nostr.event.filter.Filters;
import nostr.event.impl.GenericEvent;
-import nostr.event.message.EventMessage;
-import nostr.event.message.ReqMessage;
import nostr.id.Identity;
import nostr.util.NostrUtil;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
@NoArgsConstructor
public class NostrSpringWebSocketClient implements NostrIF {
-
- private static NostrSpringWebSocketClient INSTANCE;
-
- private List clients = new ArrayList<>();
-
+ private final Map clientMap = new ConcurrentHashMap<>();
@Getter
private Identity sender;
- private NostrSpringWebSocketClient(@NonNull Map relays) {
- relays.values().forEach(this::createClientForRelay);
- }
+ private static NostrSpringWebSocketClient INSTANCE;
- public NostrSpringWebSocketClient(@NonNull Identity sender) {
- this.sender = sender;
+ public NostrSpringWebSocketClient(String relayName, String relayUri) {
+ setRelays(Map.of(relayName, relayUri));
}
public static NostrIF getInstance() {
@@ -50,82 +41,83 @@ public static NostrIF getInstance(@NonNull Identity sender) {
return (INSTANCE == null) ? new NostrSpringWebSocketClient(sender) : INSTANCE;
}
- private void createClientForRelay(String relay) {
- clients.add(new SpringWebSocketClient(relay));
+ public NostrSpringWebSocketClient(@NonNull Identity sender) {
+ this.sender = sender;
}
- @Override
public NostrIF setSender(@NonNull Identity sender) {
this.sender = sender;
return this;
}
- @Override
- public NostrIF setRelays(@NonNull Map relays) {
- relays.values().stream()
- .filter(relay ->
- clients.stream()
- .map(SpringWebSocketClient::getRelayUrl)
- .noneMatch(
- relay::equals))
- .forEach(this::addClient);
- return this;
- }
-
- @SneakyThrows
- private void addClient(String relay) {
- clients.add(new SpringWebSocketClient(relay));
+ public List sendEvent(T event, Map relays) {
+ setRelays(relays);
+ return relays.keySet().stream().map(s ->
+ clientMap.get(s).sendEvent(event))
+ .flatMap(List::stream)
+ .distinct().toList();
}
@Override
- public void close() throws IOException {
- for (SpringWebSocketClient client : clients) {
- client.closeSocket();
- }
+ public NostrIF setRelays(@NonNull Map relays) {
+ relays.entrySet().stream().forEach(relayEntry ->
+ clientMap.putIfAbsent(relayEntry.getKey(),
+ new WebSocketClientHandler(
+ relayEntry.getKey(),
+ relayEntry.getValue())));
+ return this;
}
@Override
- public List send(@NonNull IEvent event) {
- EventMessage message = new EventMessage(event);
- return clients.stream().flatMap(client -> send(client, message).stream()).toList();
+ public List sendEvent(@NonNull IEvent event) {
+ return clientMap.values().stream().map(client ->
+ client.sendEvent(event)).flatMap(List::stream).distinct().toList();
}
-
@Override
- public List send(@NonNull IEvent event, Map relays) {
+ public List sendEvent(@NonNull IEvent event, Map relays) {
setRelays(relays);
- return send(event);
+ return sendEvent(event);
}
@Override
- public List send(@NonNull Filters filters, @NonNull String subscriptionId) {
- ReqMessage reqMessage = new ReqMessage(subscriptionId, filters);
- return clients.stream().flatMap(client -> send(client, reqMessage).stream()).toList();
+ public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) {
+ return sendRequest(List.of(filters), subscriptionId, relays);
}
@Override
- public List send(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) {
+ public List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) {
setRelays(relays);
- return send(filters, subscriptionId);
+ return sendRequest(filtersList, subscriptionId);
}
@Override
- public List send(@NonNull List filtersList, @NonNull String subscriptionId) {
- return filtersList.stream().flatMap(filters -> send(filters, subscriptionId).stream()).toList();
+ public List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId) {
+ return filtersList.stream().map(filters -> sendRequest(
+ filters,
+ subscriptionId
+ ))
+ .flatMap(List::stream)
+ .distinct().toList();
}
@Override
- public List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) {
- return filtersList.stream().flatMap(filters -> send(filters, subscriptionId, relays).stream()).toList();
- }
+ public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId) {
+ createRequestClient(subscriptionId);
- @SneakyThrows
- private List send(SpringWebSocketClient client, BaseMessage message) {
- return client.send(message);
+ return clientMap.entrySet().stream().filter(entry ->
+ entry.getValue().getRelayName().equals(String.join(entry.getKey(), subscriptionId)))
+ .map(Entry::getValue)
+ .map(webSocketClientHandler ->
+ webSocketClientHandler.sendRequest(
+ filters,
+ webSocketClientHandler.getRelayName()))
+ .flatMap(List::stream).toList();
}
+
@Override
- public List send(@NonNull BaseMessage message, @NonNull RequestContext context) {
+ public List sendRequest(@NonNull BaseMessage message, @NonNull RequestContext context) {
return List.of();
}
@@ -151,9 +143,35 @@ public boolean verify(@NonNull GenericEvent event) {
}
}
+ @Override
+ public Identity getSender() {
+ return sender;
+ }
+
@Override
public Map getRelays() {
- return clients.stream()
- .collect(Collectors.toMap(SpringWebSocketClient::getRelayUrl, SpringWebSocketClient::getRelayUrl, (prev, next) -> next, HashMap::new));
+ return clientMap.values().stream()
+ .collect(Collectors.toMap(WebSocketClientHandler::getRelayName, WebSocketClientHandler::getRelayUri,
+ (prev, next) -> next, HashMap::new));
+ }
+
+ public void close() throws IOException {
+ for (WebSocketClientHandler client : clientMap.values()) {
+ client.close();
+ }
+ }
+
+ private void createRequestClient(String subscriptionId) {
+ if (clientMap.entrySet().stream() // if a request client doesn't yet exist for subscriptionId...
+ .noneMatch(entry ->
+ entry.getValue().getRelayName().equals(String.join(entry.getKey(), subscriptionId)))) {
+ clientMap.keySet().forEach(clientMapKey -> // ... create one for each relay and add it to the client map
+ clientMap.entrySet().stream().map(entry ->
+ new WebSocketClientHandler(
+ String.join(entry.getKey(), subscriptionId),
+ entry.getValue().getRelayUri()))
+ .toList().forEach(webSocketClientHandler ->
+ clientMap.put(clientMapKey, webSocketClientHandler)));
+ }
}
}
diff --git a/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java
new file mode 100644
index 000000000..be0954b6f
--- /dev/null
+++ b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java
@@ -0,0 +1,57 @@
+package nostr.api;
+
+import lombok.Getter;
+import lombok.NonNull;
+import nostr.base.IEvent;
+import nostr.client.springwebsocket.SpringWebSocketClient;
+import nostr.event.filter.Filters;
+import nostr.event.message.EventMessage;
+import nostr.event.message.ReqMessage;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class WebSocketClientHandler {
+ private final SpringWebSocketClient eventClient;
+ private final Map requestClientMap = new ConcurrentHashMap<>();
+
+ @Getter
+ private String relayName;
+ @Getter
+ private String relayUri;
+
+ protected WebSocketClientHandler(@NonNull String relayName, @NonNull String relayUri) {
+ this.relayName = relayName;
+ this.relayUri = relayUri;
+ this.eventClient = new SpringWebSocketClient(relayUri);
+ }
+
+ protected List sendEvent(@NonNull IEvent event) {
+ return eventClient.send(new EventMessage(event)).stream().toList();
+ }
+
+ protected List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId) {
+ return Optional
+ .ofNullable(
+ requestClientMap.get(subscriptionId))
+ .map(client ->
+ client.send(new ReqMessage(subscriptionId, filters))).or(() -> {
+ requestClientMap.put(subscriptionId, new SpringWebSocketClient(relayUri));
+ return Optional.ofNullable(
+ requestClientMap.get(subscriptionId).send(
+ new ReqMessage(subscriptionId, filters)));
+ })
+ .orElse(new ArrayList<>());
+ }
+
+ public void close() throws IOException {
+ eventClient.closeSocket();
+ for (SpringWebSocketClient client : requestClientMap.values()) {
+ client.closeSocket();
+ }
+ }
+}
diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java
index 94e6211fc..98e9a679b 100644
--- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java
+++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java
@@ -17,8 +17,8 @@
import nostr.base.UserProfile;
import nostr.event.BaseTag;
import nostr.event.Marker;
+import nostr.event.filter.Filters;
import nostr.event.impl.EphemeralEvent;
-import nostr.event.impl.Filters;
import nostr.event.impl.MetadataEvent;
import nostr.event.impl.ParameterizedReplaceableEvent;
import nostr.event.impl.ReplaceableEvent;
diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java
index 08a5ffc40..2bcd9f642 100644
--- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java
+++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java
@@ -10,6 +10,7 @@
import nostr.api.factory.EventFactory;
import nostr.api.factory.TagFactory;
import nostr.event.BaseTag;
+import nostr.event.Kind;
import nostr.event.impl.GenericEvent;
import nostr.id.Identity;
@@ -22,8 +23,6 @@
*/
public class NIP23Impl {
- public static final Integer KIND_PRE_LONG_FORM_CONTENT = 30023;
-
@Data
@EqualsAndHashCode(callSuper = false)
public static class LongFormContentEventFactory extends EventFactory {
@@ -38,7 +37,7 @@ public LongFormContentEventFactory(@NonNull Identity sender, @NonNull List {
+ private final CalendarRsvpContent calendarRsvpContent;
+
+ public CalendarRsvpEventFactory(@NonNull Identity sender, @NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) {
+ super(sender, baseTags, content);
+ this.calendarRsvpContent = calendarRsvpContent;
+ }
+
+ @Override
+ public CalendarRsvpEvent create() {
+ return new CalendarRsvpEvent(getSender(), getTags(), getContent(), calendarRsvpContent);
+ }
+ }
}
diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP60Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP60Impl.java
index 54218f53f..38d5b4609 100644
--- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP60Impl.java
+++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP60Impl.java
@@ -5,6 +5,7 @@
import lombok.NonNull;
import nostr.api.factory.EventFactory;
import nostr.event.BaseTag;
+import nostr.event.Kind;
import nostr.event.impl.GenericEvent;
import nostr.id.Identity;
@@ -18,7 +19,7 @@ public WalletEventFactory(@NonNull Identity sender, List tags, @NonNull
@Override
public GenericEvent create() {
- return new GenericEvent(getIdentity().getPublicKey(), 37375, getTags(), getContent());
+ return new GenericEvent(getIdentity().getPublicKey(), Kind.WALLET, getTags(), getContent());
}
}
@@ -30,7 +31,7 @@ public TokenEventFactory(@NonNull Identity sender, List tags, @NonNull
@Override
public GenericEvent create() {
- return new GenericEvent(getIdentity().getPublicKey(), 7375, getTags(), getContent());
+ return new GenericEvent(getIdentity().getPublicKey(), Kind.WALLET_UNSPENT_PROOF, getTags(), getContent());
}
}
@@ -42,7 +43,7 @@ public SpendingHistoryEventFactory(@NonNull Identity sender, List tags,
@Override
public GenericEvent create() {
- return new GenericEvent(getIdentity().getPublicKey(), 7376, getTags(), getContent());
+ return new GenericEvent(getIdentity().getPublicKey(), Kind.WALLET_TX_HISTORY, getTags(), getContent());
}
}
@@ -54,7 +55,7 @@ public RedemptionQuoteEventFactory(@NonNull Identity sender, List tags,
@Override
public GenericEvent create() {
- return new GenericEvent(getIdentity().getPublicKey(), 7374, getTags(), getContent());
+ return new GenericEvent(getIdentity().getPublicKey(), Kind.RESERVED_CASHU_WALLET_TOKENS, getTags(), getContent());
}
}
diff --git a/nostr-java-base/pom.xml b/nostr-java-base/pom.xml
index 6ff5f6659..7a1f2e4b6 100644
--- a/nostr-java-base/pom.xml
+++ b/nostr-java-base/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-base
diff --git a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java
index 2b2238806..c11446680 100644
--- a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java
+++ b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java
@@ -2,21 +2,23 @@
package nostr.base;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
-import java.util.List;
-
/**
- *
* @author squirrel
*/
@Data
@NoArgsConstructor
+@AllArgsConstructor
public class GenericTagQuery {
-
+
private String tagName;
- private List value;
+
+ @JsonProperty
+ private String value;
@JsonIgnore
public Integer getNip() {
diff --git a/nostr-java-base/src/main/java/nostr/base/IDecoder.java b/nostr-java-base/src/main/java/nostr/base/IDecoder.java
index c1dd430be..f94d95120 100644
--- a/nostr-java-base/src/main/java/nostr/base/IDecoder.java
+++ b/nostr-java-base/src/main/java/nostr/base/IDecoder.java
@@ -1,5 +1,7 @@
package nostr.base;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
/**
*
* @author eric
@@ -7,6 +9,6 @@
*/
public interface IDecoder {
- T decode(String str);
+ T decode(String str) throws JsonProcessingException;
}
diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml
index 2ad124eea..4aa6500ba 100644
--- a/nostr-java-client/pom.xml
+++ b/nostr-java-client/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-client
jar
@@ -24,12 +24,6 @@
nostr-java-id
${project.version}
-
- ${project.groupId}
- nostr-java-connection
- ${project.version}
- compile
-
org.springframework
spring-websocket
@@ -64,4 +58,4 @@
4.2.2
-
\ No newline at end of file
+
diff --git a/nostr-java-client/src/main/java/module-info.java b/nostr-java-client/src/main/java/module-info.java
index 75a2fccc2..ff959a1f2 100644
--- a/nostr-java-client/src/main/java/module-info.java
+++ b/nostr-java-client/src/main/java/module-info.java
@@ -4,9 +4,7 @@
requires java.logging;
requires nostr.util;
requires nostr.base;
- requires nostr.connection;
requires nostr.context;
- requires nostr.context.impl;
requires com.fasterxml.jackson.core;
requires reactor.core;
requires spring.webflux;
@@ -14,9 +12,7 @@
requires spring.beans;
requires spring.websocket;
requires jakarta.websocket.client;
- requires annotations;
requires awaitility;
- exports nostr.client;
exports nostr.client.springwebsocket;
}
diff --git a/nostr-java-client/src/main/java/nostr/client/Client.java b/nostr-java-client/src/main/java/nostr/client/Client.java
deleted file mode 100644
index d1cc550f2..000000000
--- a/nostr-java-client/src/main/java/nostr/client/Client.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package nostr.client;
-
-import lombok.AllArgsConstructor;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import lombok.SneakyThrows;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-import nostr.connection.impl.ConnectionPool;
-import nostr.context.Context;
-import nostr.context.RequestContext;
-import nostr.context.impl.DefaultRequestContext;
-import nostr.event.BaseMessage;
-import nostr.event.impl.GenericTag;
-import nostr.event.message.CanonicalAuthenticationMessage;
-import nostr.util.thread.Task;
-import nostr.util.thread.ThreadUtil;
-
-import java.util.concurrent.TimeoutException;
-import java.util.logging.Level;
-
-@Log
-@NoArgsConstructor
-public class Client {
-
- private static class Holder {
- private static final Client INSTANCE = new Client();
- }
-
- private RequestContext context;
-
- private ConnectionPool connectionPool;
-
- public static Client getInstance() {
- return Holder.INSTANCE;
- }
-
- public Client connect(@NonNull RequestContext context) throws TimeoutException {
- if (context instanceof DefaultRequestContext defaultRequestContext) {
- Holder.INSTANCE.context = context;
- connectionPool = ConnectionPool.getInstance(defaultRequestContext);
- ThreadUtil.builder().blocking(true).lock(true).task(new RelayConnectionTask(this.connectionPool)).build().run(context);
- }
- return this;
- }
-
- public void disconnect() throws TimeoutException {
- ThreadUtil.builder().blocking(true).task(new RelayDisconnectionTask(this.connectionPool)).build().run(this.context);
- }
-
- public int getOpenConnectionsCount() {
- return connectionPool.connectionCount();
- }
-
- public void send(@NonNull BaseMessage message) throws TimeoutException {
- log.log(Level.FINE, "Requesting to send the message {0}...", message);
- ThreadUtil.builder().blocking(false).lock(true).task(new SendMessageTask(message, this.connectionPool)).build().run(this.context);
- }
-
- boolean isConnected(@NonNull Relay relay) {
- return this.connectionPool.isConnectedTo(relay);
- }
-
- @AllArgsConstructor
- private static class RelayConnectionTask implements Task {
-
- private final ConnectionPool connectionManager;
-
- @Override
- public Void execute(@NonNull Context context) {
- connectionManager.connect();
- return null;
- }
- }
-
- @AllArgsConstructor
- private static class RelayDisconnectionTask implements Task {
-
- private final ConnectionPool connectionPool;
-
- @Override
- public Void execute(@NonNull Context context) {
- connectionPool.disconnect();
- return null;
- }
- }
-
- @AllArgsConstructor
- private static class SendMessageTask implements Task {
-
- private final BaseMessage message;
- private final ConnectionPool connectionPool;
-
- @SneakyThrows
- @Override
- public Void execute(@NonNull Context context) {
- // Only send AUTH messages to the relay mentioned in the tag https://github.com/tcheeric/nostr-java/issues/129
- if (message instanceof CanonicalAuthenticationMessage canonicalAuthenticationMessage) {
- log.log(Level.FINE, ">>> Authentication message {0}...", canonicalAuthenticationMessage);
- var event = canonicalAuthenticationMessage.getEvent();
- var relayTag = event.getTags().stream().filter(t -> t.getCode().equalsIgnoreCase("relay")).findFirst();
- if (relayTag.isPresent()) {
- var relayTagValue = ((GenericTag) relayTag.get()).getAttributes().get(0).getValue().toString();
- log.log(Level.FINEST, "**** Relay found in CanonicalAuthenticationMessage: {0}", relayTagValue);
- var r = new Relay(relayTagValue);
- connectionPool.send(canonicalAuthenticationMessage.encode(), r);
- } else {
- log.log(Level.SEVERE, "Relay tag not found in CanonicalAuthenticationMessage. Ignoring...");
- }
- } else {
- log.log(Level.FINER, "+++ message {0}...", message);
- connectionPool.send(message.encode());
- }
- return null;
- }
- }
-}
diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java
index e71e9f06d..4f9f72647 100644
--- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java
+++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java
@@ -18,7 +18,7 @@
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
-public class ReactiveWebSocketClient implements WebSocketClient {
+public class ReactiveWebSocketClient implements WebSocketClientIF {
private final ReactorNettyWebSocketClient client;
private final URI uri;
diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java
index c685f09e2..c1647f99d 100644
--- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java
+++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java
@@ -9,27 +9,27 @@
import java.util.List;
public class SpringWebSocketClient {
- private final WebSocketClient webSocketClient;
+ private final WebSocketClientIF webSocketClientIF;
@Getter
private final String relayUrl;
public SpringWebSocketClient(@NonNull String relayUrl) {
- webSocketClient = new StandardWebSocketClient(relayUrl);
+ webSocketClientIF = new StandardWebSocketClient(relayUrl);
this.relayUrl = relayUrl;
}
@SneakyThrows
public List send(@NonNull BaseMessage eventMessage) {
- return webSocketClient.send(eventMessage.encode());
+ return webSocketClientIF.send(eventMessage.encode());
}
public List send(@NonNull String json) throws IOException {
- return webSocketClient.send(json);
+ return webSocketClientIF.send(json);
}
public void closeSocket() throws IOException {
- webSocketClient.closeSocket();
+ webSocketClientIF.closeSocket();
}
}
diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java
index 23328d426..dd2ac463c 100644
--- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java
+++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java
@@ -1,9 +1,8 @@
package nostr.client.springwebsocket;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import lombok.NonNull;
import lombok.SneakyThrows;
import nostr.event.BaseMessage;
-import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
@@ -17,15 +16,16 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import static org.awaitility.Awaitility.await;
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
-public class StandardWebSocketClient extends TextWebSocketHandler implements WebSocketClient {
+public class StandardWebSocketClient extends TextWebSocketHandler implements WebSocketClientIF {
private final WebSocketSession clientSession;
private List events = new ArrayList<>();
- private boolean completed = false;
+ private final AtomicBoolean completed = new AtomicBoolean(false);
@SneakyThrows
public StandardWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) {
@@ -33,23 +33,25 @@ public StandardWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) {
}
@Override
- protected void handleTextMessage(@NotNull WebSocketSession session, TextMessage message) {
+ protected void handleTextMessage(@NonNull WebSocketSession session, TextMessage message) {
events.add(message.getPayload());
- completed = true;
+ completed.setRelease(true);
}
@Override
- public List send(T eventMessage) throws JsonProcessingException, IOException {
+ public List send(T eventMessage) throws IOException {
return send(eventMessage.encode());
}
@Override
public List send(String json) throws IOException {
clientSession.sendMessage(new TextMessage(json));
- await().until(() -> completed);
+ await()
+// .timeout(66, TimeUnit.MINUTES)
+ .untilTrue(completed);
List eventList = List.copyOf(events);
events = new ArrayList<>();
- completed = false;
+ completed.setRelease(false);
return eventList;
}
@@ -57,4 +59,4 @@ public List send(String json) throws IOException {
public void closeSocket() throws IOException {
clientSession.close();
}
-}
\ No newline at end of file
+}
diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClientIF.java
similarity index 89%
rename from nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClient.java
rename to nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClientIF.java
index 600a4284e..75e538201 100644
--- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClient.java
+++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClientIF.java
@@ -5,7 +5,7 @@
import java.io.IOException;
import java.util.List;
-public interface WebSocketClient {
+public interface WebSocketClientIF {
List send(T eventMessage) throws IOException;
List send(String json) throws IOException;
void closeSocket() throws IOException;
diff --git a/nostr-java-command-interface/pom.xml b/nostr-java-command-interface/pom.xml
deleted file mode 100644
index a42859beb..000000000
--- a/nostr-java-command-interface/pom.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- 4.0.0
-
-
- xyz.tcheeric
- nostr-java
- 0.6.3-SNAPSHOT
-
-
- nostr-java-command-interface
- jar
-
-
- ${project.groupId}
- nostr-java-util
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-base
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-event
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-context-interface
- ${project.version}
- compile
-
-
-
\ No newline at end of file
diff --git a/nostr-java-command-interface/src/main/java/module-info.java b/nostr-java-command-interface/src/main/java/module-info.java
deleted file mode 100644
index 6664203b6..000000000
--- a/nostr-java-command-interface/src/main/java/module-info.java
+++ /dev/null
@@ -1,10 +0,0 @@
-import nostr.command.CommandHandler;
-
-module nostr.command.handler {
-
- requires nostr.context;
-
- exports nostr.command;
-
- uses CommandHandler;
-}
diff --git a/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java b/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java
deleted file mode 100644
index ca1ff6e50..000000000
--- a/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package nostr.command;
-
-import nostr.context.CommandContext;
-
-public interface CommandHandler {
-
- void handle(CommandContext context);
-}
diff --git a/nostr-java-command-provider/pom.xml b/nostr-java-command-provider/pom.xml
deleted file mode 100644
index 8ab9e4a0d..000000000
--- a/nostr-java-command-provider/pom.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
- 4.0.0
-
-
- xyz.tcheeric
- nostr-java
- 0.6.3-SNAPSHOT
-
-
- nostr-java-command-provider
- jar
-
-
-
- ${project.groupId}
- nostr-java-id
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-client
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-context
- ${project.version}
- compile
-
-
- ${project.groupId}
- nostr-java-command-interface
- ${project.version}
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/nostr-java-command-provider/src/main/java/module-info.java b/nostr-java-command-provider/src/main/java/module-info.java
deleted file mode 100644
index 34f9f62c2..000000000
--- a/nostr-java-command-provider/src/main/java/module-info.java
+++ /dev/null
@@ -1,31 +0,0 @@
-import nostr.command.provider.AuthCommandHandler;
-import nostr.command.provider.ClosedCommandHandler;
-import nostr.command.provider.EoseCommandHandler;
-import nostr.command.provider.EventCommandHandler;
-import nostr.command.provider.NoticeCommandHandler;
-import nostr.command.provider.OkCommandHandler;
-import nostr.command.CommandHandler;
-
-module nostr.command.provider {
- requires static lombok;
- requires java.logging;
-
- requires nostr.base;
- requires nostr.id;
- requires nostr.client;
- requires nostr.event;
- requires nostr.context;
- requires nostr.context.impl;
- requires nostr.command.handler;
- requires com.fasterxml.jackson.core;
-
- exports nostr.command.provider;
-
- provides CommandHandler with
- OkCommandHandler,
- NoticeCommandHandler,
- EoseCommandHandler,
- AuthCommandHandler,
- EventCommandHandler,
- ClosedCommandHandler;
-}
diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java
deleted file mode 100644
index 001c29f54..000000000
--- a/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package nostr.command.provider;
-
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import lombok.SneakyThrows;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.PrivateKey;
-import nostr.base.annotation.DefaultHandler;
-import nostr.client.Client;
-import nostr.command.CommandHandler;
-import nostr.context.CommandContext;
-import nostr.context.impl.DefaultCommandContext;
-import nostr.event.impl.CanonicalAuthenticationEvent;
-import nostr.event.message.CanonicalAuthenticationMessage;
-import nostr.event.message.RelayAuthenticationMessage;
-import nostr.id.Identity;
-
-import java.util.logging.Level;
-
-@DefaultHandler(command = Command.AUTH)
-@NoArgsConstructor
-@Log
-public class AuthCommandHandler implements CommandHandler {
-
- @SneakyThrows
- @Override
- public void handle(@NonNull CommandContext context) {
-
- log.log(Level.INFO, "onAuth event - {0}", context);
-
- if (context instanceof DefaultCommandContext defaultCommandContext) {
- var message = defaultCommandContext.getMessage();
-
- if (message instanceof RelayAuthenticationMessage) {
- log.log(Level.INFO, "Authentication required on relay {0}", defaultCommandContext.getRelay());
-
- var privateKey = defaultCommandContext.getPrivateKey();
- var identity = Identity.create(new PrivateKey(privateKey));
- var publicKey = identity.getPublicKey();
- var challenge = defaultCommandContext.getChallenge();
- var relay = defaultCommandContext.getRelay();
-
- // Create the event
- var canonicalAuthenticationEvent = new CanonicalAuthenticationEvent(publicKey, challenge, relay);
-
- // Sign the event
- identity.sign(canonicalAuthenticationEvent);
-
- var client = Client.getInstance();
- var canonicalAuthenticationMessage = new CanonicalAuthenticationMessage(canonicalAuthenticationEvent);
- var encodedMessage = canonicalAuthenticationMessage.encode();
- log.log(Level.INFO, "Sending authentication event {0} to the relay {1}", new Object[]{encodedMessage, relay});
-
- // Publish the event to the relay
- client.send(canonicalAuthenticationMessage);
- }
- } else {
- throw new IllegalArgumentException("Invalid context type");
- }
- }
-}
diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java
deleted file mode 100644
index 20400e981..000000000
--- a/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package nostr.command.provider;
-
-import lombok.NoArgsConstructor;
-import lombok.SneakyThrows;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.PrivateKey;
-import nostr.base.annotation.DefaultHandler;
-import nostr.client.Client;
-import nostr.command.CommandHandler;
-import nostr.context.CommandContext;
-import nostr.context.impl.DefaultCommandContext;
-import nostr.event.impl.CanonicalAuthenticationEvent;
-import nostr.event.message.CanonicalAuthenticationMessage;
-import nostr.event.message.ClosedMessage;
-import nostr.id.Identity;
-
-import java.util.logging.Level;
-
-@DefaultHandler(command = Command.CLOSED)
-@NoArgsConstructor
-@Log
-public class ClosedCommandHandler implements CommandHandler {
-
- @SneakyThrows
- @Override
- public void handle(CommandContext context) {
-
- log.info("onClosed event {0}" + context);
-
- if (context instanceof DefaultCommandContext defaultCommandContext) {
- var message = defaultCommandContext.getMessage();
-
- if (message instanceof ClosedMessage closedMessage) {
- if (closedMessage.getMessage().startsWith("auth-required:")) {
- log.log(Level.INFO, "Authentication required on relay {0}", defaultCommandContext.getRelay());
-
- var privateKey = defaultCommandContext.getPrivateKey();
- var identity = Identity.create(new PrivateKey(privateKey));
- var publicKey = identity.getPublicKey();
- var challenge = defaultCommandContext.getChallenge();
- var relay = defaultCommandContext.getRelay();
-
- // Create the event
- var canonicalAuthenticationEvent = new CanonicalAuthenticationEvent(publicKey, challenge, relay);
-
- // Sign the event
- identity.sign(canonicalAuthenticationEvent);
-
- var client = Client.getInstance(); // No need to pass the request context here. The client will use the default one
- var canonicalAuthenticationMessage = new CanonicalAuthenticationMessage(canonicalAuthenticationEvent);
- var encodedMessage = canonicalAuthenticationMessage.encode();
- log.log(Level.INFO, "Sending authentication event {0} to the relay {1}", new Object[]{encodedMessage, relay});
-
- // Publish the event to the relay
- client.send(canonicalAuthenticationMessage);
- }
- }
- } else {
- throw new IllegalArgumentException("Invalid context type");
- }
- }
-}
diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java
deleted file mode 100644
index 3b9df1846..000000000
--- a/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package nostr.command.provider;
-
-import lombok.NoArgsConstructor;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.annotation.DefaultHandler;
-import nostr.command.CommandHandler;
-import nostr.context.CommandContext;
-
-import java.util.logging.Level;
-
-@Log
-@DefaultHandler(command = Command.EOSE)
-@NoArgsConstructor
-public class EoseCommandHandler implements CommandHandler {
-
- @Override
- public void handle(CommandContext context) {
- log.log(Level.INFO, "onEose event - {0}", context);
- }
-}
diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java
deleted file mode 100644
index ac2fb75fc..000000000
--- a/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package nostr.command.provider;
-
-import lombok.NoArgsConstructor;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.annotation.DefaultHandler;
-import nostr.command.CommandHandler;
-import nostr.context.CommandContext;
-
-import java.util.logging.Level;
-
-@Log
-@DefaultHandler(command = Command.EVENT)
-@NoArgsConstructor
-public class EventCommandHandler implements CommandHandler {
-
- @Override
- public void handle(CommandContext context) {
- log.log(Level.INFO, "onEvent event - {0}", context);
- }
-}
diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java
deleted file mode 100644
index 22d862036..000000000
--- a/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package nostr.command.provider;
-
-import lombok.NoArgsConstructor;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.annotation.DefaultHandler;
-import nostr.command.CommandHandler;
-import nostr.context.CommandContext;
-
-import java.util.logging.Level;
-
-@Log
-@DefaultHandler(command = Command.NOTICE)
-@NoArgsConstructor
-public class NoticeCommandHandler implements CommandHandler {
- @Override
- public void handle(CommandContext context) {
- log.log(Level.INFO, "onNotice event - {0}", context);
- }
-}
diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java
deleted file mode 100644
index 5f2c1d982..000000000
--- a/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package nostr.command.provider;
-
-import lombok.NoArgsConstructor;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.annotation.DefaultHandler;
-import nostr.command.CommandHandler;
-import nostr.context.CommandContext;
-
-import java.util.logging.Level;
-
-@Log
-@DefaultHandler(command = Command.OK)
-@NoArgsConstructor
-public class OkCommandHandler implements CommandHandler {
-
- @Override
- public void handle(CommandContext context) {
- log.log(Level.INFO, "onOk event - {0}", context);
- }
-}
diff --git a/nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler b/nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler
deleted file mode 100644
index 723510a03..000000000
--- a/nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler
+++ /dev/null
@@ -1,6 +0,0 @@
-nostr.command.provider.EventCommandHandler
-nostr.command.provider.AuthCommandHandler
-nostr.command.provider.EoseCommandHandler
-nostr.command.provider.NoticeCommandHandler
-nostr.command.provider.OkCommandHandler
-nostr.command.provider.ClosedCommandHandler
\ No newline at end of file
diff --git a/nostr-java-connection/pom.xml b/nostr-java-connection/pom.xml
deleted file mode 100644
index f27b6bfd8..000000000
--- a/nostr-java-connection/pom.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
- 4.0.0
-
-
- xyz.tcheeric
- nostr-java
- 0.6.3-SNAPSHOT
-
-
- nostr-java-connection
- jar
-
-
-
-
- ${project.groupId}
- nostr-java-event
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-util
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-controller
- ${project.version}
- compile
-
-
- com.squareup.okio
- okio
- ${okio.version}
-
-
- com.squareup.okhttp3
- okhttp
- ${okhttp.version}
-
-
-
-
diff --git a/nostr-java-connection/src/main/java/module-info.java b/nostr-java-connection/src/main/java/module-info.java
deleted file mode 100644
index 974504604..000000000
--- a/nostr-java-connection/src/main/java/module-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-
-module nostr.connection {
-
- requires static lombok;
-
- requires okio;
- requires okhttp3;
-
- requires java.logging;
-
- requires nostr.base;
- requires nostr.event;
- requires nostr.controller;
- requires nostr.context;
- requires nostr.context.impl;
- requires org.bouncycastle.provider;
-
- exports nostr.connection;
- exports nostr.connection.impl;
- exports nostr.connection.impl.listeners;
-}
diff --git a/nostr-java-connection/src/main/java/nostr/connection/Connection.java b/nostr-java-connection/src/main/java/nostr/connection/Connection.java
deleted file mode 100644
index b6c328e95..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/Connection.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nostr.connection;
-
-import nostr.base.Relay;
-
-public interface Connection {
-
- void send(String message);
-
- void connect();
-
- void disconnect();
-
- Relay getRelay();
-
- boolean isConnected();
-
-}
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java b/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java
deleted file mode 100644
index 61c1640c6..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package nostr.connection.impl;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.ToString;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-import nostr.connection.Connection;
-import nostr.connection.impl.listeners.CloseListener;
-import nostr.connection.impl.listeners.CompositeListener;
-import nostr.connection.impl.listeners.ErrorListener;
-import nostr.connection.impl.listeners.OpenListener;
-import nostr.connection.impl.listeners.TextListener;
-import nostr.context.Context;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.WebSocket;
-
-import java.util.Arrays;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-
-@Log
-@ToString(onlyExplicitlyIncluded = true)
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
-public class ConnectionImpl implements Connection {
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private final String id = UUID.randomUUID().toString();
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private final Relay relay;
-
- private final Context context;
-
- private WebSocket webSocket = null;
-
- private final AtomicBoolean connected = new AtomicBoolean(false);
-
- public ConnectionImpl(@NonNull Relay relay, @NonNull Context context) {
- this.relay = relay;
- this.context = context;
- }
-
- @Override
- public void connect() {
- var relay = getRelay();
-
- try {
- if (isConnected()) {
- log.log(Level.FINE, "Already connected to {0}. Do nothing...", relay);
- return;
- }
-
- log.log(Level.INFO, "Connecting to {0}... httpUrl = {1}", new Object[]{relay, HttpUrl.parse(relay.getHttpUri())});
- var client = new OkHttpClient();
- var compositeListener = new CompositeListener(Arrays.asList(new OpenListener(relay), new TextListener(relay, context), new CloseListener(relay), new ErrorListener(relay)));
-
- webSocket = client.newWebSocket((new Request.Builder()).url(HttpUrl.parse(relay.getHttpUri())).build(), compositeListener);
- //.connectTimeout(Duration.ofMillis(10000)) // TODO - make this configurable and add to the context.
-
- connected.set(true);
-
- log.log(Level.INFO, "Connected to {0}", getRelay());
- } catch (Exception e) {
- log.log(Level.SEVERE, "Failed to connect to {0} - {1}", new Object[]{relay, e.getMessage()});
- connected.set(false);
- }
- }
-
- @Override
- public boolean isConnected() {
- return connected.get() && webSocket != null;
- }
-
- @Override
- public void send(@NonNull String message) {
- if (!isConnected()) {
- log.log(Level.WARNING, "FAIL - Not properly connected with Relay: {0}", relay);
- return;
- }
-
- log.log(Level.INFO, "Sending message: {0} - Relay: {1}", new Object[]{message, relay});
- webSocket.send(message);
- }
-
- @Override
- public void disconnect() {
- if (isConnected()) {
- log.log(Level.INFO, "Disconnecting from {0}", relay);
- // NORMAL_CLOSURE is 1000
- webSocket.close(1000, "bye"); // TODO check return result of close
- }
- connected.set(false);
- }
-}
\ No newline at end of file
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java b/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java
deleted file mode 100644
index 44e144813..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package nostr.connection.impl;
-
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-import nostr.connection.Connection;
-import nostr.context.Context;
-import nostr.context.impl.DefaultRequestContext;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-
-@Getter
-@Log
-public class ConnectionPool {
-
- private static class Holder {
- private static final ConnectionPool INSTANCE = new ConnectionPool();
- }
-
- private final Set connections = Collections.synchronizedSet(new HashSet<>());
-
- private ConnectionPool() {
- // private constructor to prevent instantiation
- }
-
- public static ConnectionPool getInstance(@NonNull Context context) {
- if (Holder.INSTANCE.connections.isEmpty() && context instanceof DefaultRequestContext defaultRequestContext) {
- var relays = defaultRequestContext.getRelays();
- relays.values().stream().map(Relay::new).forEach(r -> Holder.INSTANCE.addConnection(new ConnectionImpl(r, context)));
- }
- return Holder.INSTANCE;
- }
-
- public void connect() {
- log.log(Level.INFO, "Connecting to relays");
- connections.forEach(c -> {
- c.connect();
- });
- }
-
- public void disconnect() {
- log.log(Level.INFO, "Disconnecting from relays");
- connections.forEach(connection -> {
- connection.disconnect();
- });
- }
-
- public void disconnect(@NonNull Relay relay) {
- log.log(Level.INFO, "Disconnecting from {0}...", relay);
- var connection = getConnection(relay);
- if (connection != null) {
- connection.disconnect();
- } else {
- log.log(Level.WARNING, "No connection found for {0}. Ignoring...", relay);
- }
- }
-
- public Connection getConnection(@NonNull Relay relay) {
- return connections.stream().filter(c -> c.getRelay().getUri().equalsIgnoreCase(relay.getUri())).findFirst().orElse(null);
- }
-
- public boolean isConnectedTo(@NonNull Relay relay) {
- return connections.stream().filter(c -> c.getRelay().equals(relay)).anyMatch(Connection::isConnected);
- }
-
- public int connectionCount() {
- return (int) connections.stream().filter(Connection::isConnected).count();
- }
-
- public void send(@NonNull String message) {
- log.log(Level.FINER, "Connected to {0} relay(s)...", connections.size());
- connections.forEach(conn -> {
- conn.send(message);
- });
- }
-
- public void send(@NonNull String message, @NonNull Relay relay) {
- log.log(Level.FINER, "ConnectionPool.send({0}, {1}) / {2} connection(s)", new Object[]{message, relay, connections.size()});
- var connection = getConnection(relay);
- if (connection != null) {
- log.log(Level.FINE, "Trying to send {0} to {1}...", new Object[]{message, relay});
- connection.send(message);
- }
- else {
- log.log(Level.WARNING, "No connection found for {0}", relay);
- }
- }
-
- private boolean addConnection(@NonNull Connection connection) {
- return connections.add(connection);
- }
-
- private boolean removeConnection(@NonNull Connection connection) {
- return connections.remove(connection);
- }
-
- private void removeAllConnections() {
- connections.clear();
- }
-
-}
\ No newline at end of file
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java
deleted file mode 100644
index f064bee5c..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package nostr.connection.impl.listeners;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.ToString;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-import nostr.event.Response;
-
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-
-@AllArgsConstructor
-@Log
-public class CloseListener extends WebSocketListener {
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private final Relay relay;
-
- private final Set responses = Collections.synchronizedSet(new HashSet<>());
-
- @Override
- public void onClosed(WebSocket webSocket, int statusCode, String reason) {
- log.log(Level.INFO, "WebSocket connection to {0} closed: {1}, {2}", new Object[]{relay, statusCode, reason});
- responses.clear();
- }
-
- @Override
- public void onClosing(WebSocket webSocket, int statusCode, String reason) {
- log.log(Level.INFO, "WebSocket connection to {0} closing: {1}, {2}", new Object[]{relay, statusCode, reason});
- responses.clear();
- }
-}
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java
deleted file mode 100644
index a7f696bca..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package nostr.connection.impl.listeners;
-
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import okhttp3.Response;
-import okio.ByteString;
-
-import java.util.List;
-
-public class CompositeListener extends WebSocketListener {
- private final List listeners;
-
- public CompositeListener(List listeners) {
- this.listeners = listeners;
- }
-
- @Override
- public void onOpen(WebSocket webSocket, Response response) {
- listeners.forEach(listener -> listener.onOpen(webSocket, response));
- }
-
- @Override
- public void onMessage(WebSocket webSocket, String data) {
- listeners.forEach(listener -> listener.onMessage(webSocket, data));
- }
-
- @Override
- public void onMessage(WebSocket webSocket, ByteString bytes) {
- listeners.forEach(listener -> listener.onMessage(webSocket, bytes));
- }
-
- @Override
- public void onClosing(WebSocket webSocket, int statusCode, String reason) {
- listeners.forEach(listener -> listener.onClosing(webSocket, statusCode, reason));
- }
-
- @Override
- public void onClosed(WebSocket webSocket, int statusCode, String reason) {
- listeners.forEach(listener -> listener.onClosed(webSocket, statusCode, reason));
- }
-
- @Override
- public void onFailure(WebSocket webSocket, Throwable error, Response response) {
- listeners.forEach(listener -> listener.onFailure(webSocket, error, response));
- }
-}
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java
deleted file mode 100644
index 607159813..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package nostr.connection.impl.listeners;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.ToString;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-
-import okhttp3.WebSocketListener;
-import okhttp3.WebSocket;
-import okhttp3.Response;
-import java.util.logging.Level;
-
-@Getter
-@Log
-@AllArgsConstructor
-public class ErrorListener extends WebSocketListener {
-
- @EqualsAndHashCode.Include
- @ToString.Include
- private final Relay relay;
-
- @Override
- public void onFailure(WebSocket webSocket, Throwable error, Response response) {
- log.log(Level.WARNING, "WebSocket error: {0} - Relay {1}", new Object[]{error, relay});
- error.printStackTrace();
- }
-}
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java
deleted file mode 100644
index fb3e69d74..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package nostr.connection.impl.listeners;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.ToString;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import okhttp3.Response;
-import java.util.logging.Level;
-
-@Getter
-@AllArgsConstructor
-@Log
-public class OpenListener extends WebSocketListener {
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private final Relay relay;
-
- @Override
- public void onOpen(WebSocket webSocket, Response response) {
- log.log(Level.INFO, "WebSocket opened to {0}", relay);
- }
-}
diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java
deleted file mode 100644
index 7695e8a7e..000000000
--- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package nostr.connection.impl.listeners;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.ToString;
-import lombok.extern.java.Log;
-import nostr.base.Relay;
-import nostr.context.CommandContext;
-import nostr.context.Context;
-import nostr.context.impl.DefaultCommandContext;
-import nostr.context.impl.DefaultRequestContext;
-import nostr.controller.impl.ApplicationControllerImpl;
-import nostr.event.BaseMessage;
-import nostr.event.json.codec.BaseMessageDecoder;
-import nostr.event.message.ClosedMessage;
-import nostr.event.message.RelayAuthenticationMessage;
-
-import okhttp3.WebSocketListener;
-import okhttp3.WebSocket;
-import okio.ByteString;
-
-import java.util.logging.Level;
-
-@AllArgsConstructor
-@Log
-public class TextListener extends WebSocketListener {
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private final Relay relay;
-
- private Context context;
-
- @Override
- public void onMessage(WebSocket webSocket, String message) {
- // nocheckin
- log.log(Level.INFO, "WebSocket received: " + message + " - Relay: {0}", new Object[]{relay});
- handleReceivedText(message);
- }
-
- @Override
- public void onMessage(WebSocket webSocket, ByteString bytes) {
- String message = bytes.toString();
- log.log(Level.INFO, "WebSocket received: {0} - Relay: {1}", new Object[]{message, relay});
- handleReceivedText(message);
- }
-
- private void handleReceivedText(@NonNull String message) {
- log.log(Level.INFO, "Received message {0} from {1}", new Object[]{message, relay});
-
- var msg = new BaseMessageDecoder<>().decode(message);
- final String strCommand = msg.getCommand();
-
- log.log(Level.FINE, "Creating the command context with message {0}", new Object[]{msg});
- Context commandContext = createCommandContext(msg);
- var applicationController = new ApplicationControllerImpl(strCommand);
-
- applicationController.handleRequest(commandContext);
- }
-
- private CommandContext createCommandContext(@NonNull BaseMessage message) {
- var commandContext = new DefaultCommandContext();
-
- // Pass on the message
- commandContext.setMessage(message);
-
- // Pass on the private key
- commandContext.setPrivateKey(((DefaultRequestContext) this.context).getPrivateKey());
-
- // Pass on the first relay
- commandContext.setRelay(relay);
-
- // Set the challenge
- if (message instanceof RelayAuthenticationMessage authMessage) {
- log.log(Level.FINE, "Setting the challenge {0} for the relay {1}", new Object[]{authMessage.getChallenge(), relay});
- commandContext.setChallenge(authMessage.getChallenge());
- }
-
- if (message instanceof ClosedMessage) {
- commandContext.setChallenge(((DefaultRequestContext) this.context).getChallenge(relay));
- }
-
- return commandContext;
- }
-}
diff --git a/nostr-java-context-interface/pom.xml b/nostr-java-context-interface/pom.xml
deleted file mode 100644
index f4393d987..000000000
--- a/nostr-java-context-interface/pom.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
- 4.0.0
-
- xyz.tcheeric
- nostr-java
- 0.6.3-SNAPSHOT
-
-
- nostr-java-context-interface
- jar
-
- nostr-java-context-interface
- http://maven.apache.org
-
-
- UTF-8
-
-
-
-
-
diff --git a/nostr-java-context-interface/src/main/java/module-info.java b/nostr-java-context-interface/src/main/java/module-info.java
deleted file mode 100644
index 739d96fc7..000000000
--- a/nostr-java-context-interface/src/main/java/module-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-module nostr.context {
-
- exports nostr.context;
-}
\ No newline at end of file
diff --git a/nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java b/nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java
deleted file mode 100644
index 0a6413171..000000000
--- a/nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package nostr.context;
-
-public interface CommandContext extends Context {
-}
diff --git a/nostr-java-context-interface/src/main/java/nostr/context/Context.java b/nostr-java-context-interface/src/main/java/nostr/context/Context.java
deleted file mode 100644
index 66ca9e9b6..000000000
--- a/nostr-java-context-interface/src/main/java/nostr/context/Context.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package nostr.context;
-
-public interface Context {
-
- void validate();
-
- enum Type {
- REQUEST,
- COMMAND
- }
-}
diff --git a/nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java b/nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java
deleted file mode 100644
index 9b3aac647..000000000
--- a/nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package nostr.context;
-
-public interface RequestContext extends Context {
-}
diff --git a/nostr-java-context/pom.xml b/nostr-java-context/pom.xml
deleted file mode 100644
index 6c30861e0..000000000
--- a/nostr-java-context/pom.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
- 4.0.0
-
- xyz.tcheeric
- nostr-java
- 0.6.3-SNAPSHOT
-
-
- nostr-java-context
- jar
-
- nostr-java-context
- http://maven.apache.org
-
-
- UTF-8
-
-
-
-
- org.projectlombok
- lombok
-
-
- ${project.groupId}
- nostr-java-event
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-base
- ${project.version}
-
-
- ${project.groupId}
- nostr-java-context-interface
- ${project.version}
- compile
-
-
-
diff --git a/nostr-java-context/src/main/java/module-info.java b/nostr-java-context/src/main/java/module-info.java
deleted file mode 100644
index d8d637eb1..000000000
--- a/nostr-java-context/src/main/java/module-info.java
+++ /dev/null
@@ -1,9 +0,0 @@
-module nostr.context.impl {
- requires lombok;
-
- requires nostr.context;
- requires nostr.event;
- requires nostr.base;
-
- exports nostr.context.impl;
-}
\ No newline at end of file
diff --git a/nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java b/nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java
deleted file mode 100644
index 8949f63b8..000000000
--- a/nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package nostr.context.impl;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import nostr.base.Relay;
-import nostr.context.CommandContext;
-import nostr.event.BaseMessage;
-
-@Data
-@NoArgsConstructor
-public class DefaultCommandContext implements CommandContext {
- private BaseMessage message;
- private String challenge;
- private Relay relay;
- private byte[] privateKey;
-
- @Override
- public void validate() {
-
- }
-}
diff --git a/nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java b/nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java
deleted file mode 100644
index 5f0a4b0f0..000000000
--- a/nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package nostr.context.impl;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import nostr.base.Relay;
-import nostr.context.RequestContext;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@Data
-@NoArgsConstructor
-public class DefaultRequestContext implements RequestContext {
- private byte[] privateKey;
- private String subscriptionId;
- private Map relays;
- private Map challenges = new HashMap<>();
-
- @Override
- public void validate() {
- }
-
- public String getChallenge(@NonNull Relay relay) {
- return challenges.get(relay);
- }
-
- public String getChallenge(String relay) {
- return getChallenge(new Relay(relay));
- }
-
- public void setChallenge(@NonNull Relay relay, @NonNull String challenge) {
- challenges.put(relay, challenge);
- }
-
- public void setChallenge(String relay, String challenge) {
- setChallenge(new Relay(relay), challenge);
- }
-}
\ No newline at end of file
diff --git a/nostr-java-controller/pom.xml b/nostr-java-controller/pom.xml
deleted file mode 100644
index c5de9dd59..000000000
--- a/nostr-java-controller/pom.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
- 4.0.0
-
- xyz.tcheeric
- nostr-java
- 0.6.3-SNAPSHOT
-
-
- nostr-java-controller
- jar
-
- nostr-java-controller-command
- http://maven.apache.org
-
-
- UTF-8
-
-
-
-
- ${project.groupId}
- nostr-java-event
- ${project.version}
- compile
-
-
- ${project.groupId}
- nostr-java-context
- ${project.version}
- compile
-
-
- ${project.groupId}
- nostr-java-command-interface
- ${project.version}
- compile
-
-
- ${project.groupId}
- nostr-java-base
- ${project.version}
- compile
-
-
-
diff --git a/nostr-java-controller/src/main/java/module-info.java b/nostr-java-controller/src/main/java/module-info.java
deleted file mode 100644
index 2fa81f3c5..000000000
--- a/nostr-java-controller/src/main/java/module-info.java
+++ /dev/null
@@ -1,18 +0,0 @@
-import nostr.command.CommandHandler;
-
-module nostr.controller {
- uses CommandHandler;
-
- requires static lombok;
-
- requires java.logging;
-
- requires nostr.context;
- requires nostr.context.impl;
- requires nostr.base;
- requires nostr.util;
- requires nostr.command.handler;
-
- exports nostr.controller.impl;
- exports nostr.controller;
-}
\ No newline at end of file
diff --git a/nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java b/nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java
deleted file mode 100644
index 3dbb465a4..000000000
--- a/nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package nostr.controller;
-
-public interface ApplicationController extends Controller {
-}
diff --git a/nostr-java-controller/src/main/java/nostr/controller/Controller.java b/nostr-java-controller/src/main/java/nostr/controller/Controller.java
deleted file mode 100644
index 63764c470..000000000
--- a/nostr-java-controller/src/main/java/nostr/controller/Controller.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package nostr.controller;
-
-import nostr.context.Context;
-import nostr.util.thread.Task;
-
-public interface Controller extends Task {
-
- void initialize();
-
- void handleRequest(Context requestContext);
-}
diff --git a/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java b/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java
deleted file mode 100644
index 3421c9ff7..000000000
--- a/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package nostr.controller.impl;
-
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.extern.java.Log;
-import nostr.base.Command;
-import nostr.base.annotation.CustomHandler;
-import nostr.base.annotation.DefaultHandler;
-import nostr.command.CommandHandler;
-import nostr.context.Context;
-import nostr.context.impl.DefaultCommandContext;
-import nostr.controller.ApplicationController;
-import nostr.util.thread.ThreadUtil;
-
-import java.util.Optional;
-import java.util.ServiceLoader;
-import java.util.concurrent.TimeoutException;
-
-@Getter
-@Log
-public class ApplicationControllerImpl implements ApplicationController {
-
- private final String command;
-
- public ApplicationControllerImpl(@NonNull String command) {
- this.command = command;
- }
-
- @Override
- public void initialize() {
- }
-
- @Override
- public void handleRequest(Context requestContext) {
- requestContext.validate();
- if (requestContext instanceof DefaultCommandContext defaultCommandContext) {
- try {
- ThreadUtil.builder().blocking(true).task(this).build().run(defaultCommandContext);
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- @Override
- public Void execute(@NonNull Context context) {
- if (context instanceof DefaultCommandContext commandContext) {
- executeCommand(commandContext);
- }
- return null;
- }
-
- private void executeCommand(@NonNull DefaultCommandContext defaultCommandContext) {
-
- var commandHandler = getCommandHandler(this.command);
-
- log.fine("Executing command: " + this.command);
- commandHandler.handle(defaultCommandContext);
- }
-
- private static CommandHandler getCommandHandler(@NonNull String command) {
- ServiceLoader loader = ServiceLoader.load(CommandHandler.class);
-
- Optional customHandler = loader.stream()
- .map(ServiceLoader.Provider::get)
- .filter(ch -> ch.getClass().isAnnotationPresent(CustomHandler.class) && ch.getClass().getAnnotation(CustomHandler.class).command().equals(Command.valueOf(command.toUpperCase())))
- .findFirst();
-
- if (customHandler.isPresent()) {
- return customHandler.get();
- }
-
- Optional defaultHandler = loader.stream()
- .map(ServiceLoader.Provider::get)
- .filter(ch -> ch.getClass().isAnnotationPresent(DefaultHandler.class) && ch.getClass().getAnnotation(DefaultHandler.class).command().equals(Command.valueOf(command.toUpperCase())))
- .findFirst();
-
- if (defaultHandler.isPresent()) {
- return defaultHandler.get();
- }
-
- throw new AssertionError("Could not load the default handler");
- }
-}
diff --git a/nostr-java-crypto/pom.xml b/nostr-java-crypto/pom.xml
index bade534fb..ce4557a33 100644
--- a/nostr-java-crypto/pom.xml
+++ b/nostr-java-crypto/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-crypto
@@ -37,4 +37,4 @@
${project.version}
-
\ No newline at end of file
+
diff --git a/nostr-java-encryption-nip04/pom.xml b/nostr-java-encryption-nip04/pom.xml
index c2dcce47a..ced97d6bf 100644
--- a/nostr-java-encryption-nip04/pom.xml
+++ b/nostr-java-encryption-nip04/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-encryption-nip04
diff --git a/nostr-java-encryption-nip44/pom.xml b/nostr-java-encryption-nip44/pom.xml
index aa83e53f4..bb95d4948 100644
--- a/nostr-java-encryption-nip44/pom.xml
+++ b/nostr-java-encryption-nip44/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-encryption-nip44
diff --git a/nostr-java-encryption/pom.xml b/nostr-java-encryption/pom.xml
index a236f3f18..169f58443 100644
--- a/nostr-java-encryption/pom.xml
+++ b/nostr-java-encryption/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-encryption
diff --git a/nostr-java-event/pom.xml b/nostr-java-event/pom.xml
index 6e8893ef4..8a7b7c9c5 100644
--- a/nostr-java-event/pom.xml
+++ b/nostr-java-event/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-event
diff --git a/nostr-java-event/src/main/java/module-info.java b/nostr-java-event/src/main/java/module-info.java
index 607f39c9e..60e80d4b3 100644
--- a/nostr-java-event/src/main/java/module-info.java
+++ b/nostr-java-event/src/main/java/module-info.java
@@ -18,4 +18,5 @@
exports nostr.event.json.serializer;
exports nostr.event.tag;
exports nostr.event.util;
+ exports nostr.event.filter;
}
diff --git a/nostr-java-event/src/main/java/nostr/event/Kind.java b/nostr-java-event/src/main/java/nostr/event/Kind.java
index 25fae61e5..0f34b990b 100644
--- a/nostr-java-event/src/main/java/nostr/event/Kind.java
+++ b/nostr-java-event/src/main/java/nostr/event/Kind.java
@@ -29,21 +29,25 @@ public enum Kind {
MUTE_USER(44, "mute_user"),
ENCRYPTED_PAYLOADS(44, "encrypted_payloads"),
OTS_EVENT(1040, "ots_event"),
+ RESERVED_CASHU_WALLET_TOKENS(7_374, "reserved_cashu_wallet_tokens"),
+ WALLET_UNSPENT_PROOF(7_375, "wallet_unspent_proof"),
+ WALLET_TX_HISTORY(7_376, "wallet_tx_history"),
ZAP_REQUEST(9734, "zap_request"),
ZAP_RECEIPT(9735, "zap_receipt"),
REPLACEABLE_EVENT(10_000, "replaceable_event"),
+ PIN_LIST(10_001, "pin_list"),
EPHEMEREAL_EVENT(20_000, "ephemereal_event"),
CLIENT_AUTH(22_242, "authentication_of_clients_to_relays"),
+ STALL_CREATE_OR_UPDATE(30_017, "create_or_update_stall"),
PRODUCT_CREATE_OR_UPDATE(30_018, "create_or_update_product"),
+ PRE_LONG_FORM_CONTENT(30_023, "long_form_content"),
CLASSIFIED_LISTING(30_402, "classified_listing_active"),
CLASSIFIED_LISTING_INACTIVE(30_403, "classified_listing_inactive"),
CLASSIFIED_LISTING_DRAFT(30_403, "classified_listing_draft"),
CALENDAR_DATE_BASED_EVENT(31_922, "calendar_date_based_event"),
CALENDAR_TIME_BASED_EVENT(31_923, "calendar_time_based_event"),
- WALLET(37_375, "wallet"),
- WALLET_UNSPENT_PROOF(7_375, "wallet_unspent_proof"),
- WALLET_TX_HISTORY(7_376, "wallet_tx_history"),
- UNDEFINED(-1, "undefined");
+ CALENDAR_RSVP_EVENT(31_925, "calendar_rsvp_event"),
+ WALLET(37_375, "wallet");
@JsonValue
private final int value;
@@ -52,7 +56,7 @@ public enum Kind {
@JsonCreator
public static Kind valueOf(int value) {
- if (!ValueRange.of(0, 65535).isValidIntValue(value)) {
+ if (!ValueRange.of(0, 65_535).isValidIntValue(value)) {
throw new IllegalArgumentException(String.format("Kind must be between 0 and 65535 but was [%d]", value));
}
for (Kind k : values()) {
@@ -61,7 +65,7 @@ public static Kind valueOf(int value) {
}
}
- return UNDEFINED;
+ return TEXT_NOTE;
}
@Override
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java
new file mode 100644
index 000000000..b6181320b
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java
@@ -0,0 +1,79 @@
+package nostr.event.filter;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+import nostr.base.PublicKey;
+import nostr.event.impl.GenericEvent;
+import nostr.event.tag.AddressTag;
+import nostr.event.tag.IdentifierTag;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@EqualsAndHashCode
+public class AddressableTagFilter implements Filterable {
+ public final static String filterKey = "#a";
+ private final T addressableTag;
+
+ public AddressableTagFilter(T addressableTag) {
+ this.addressableTag = addressableTag;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return this::compare;
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return addressableTag;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ public static AddressTag createAddressTag(@NonNull JsonNode addressableTag) throws IllegalArgumentException {
+ try {
+ List list = Arrays.stream(addressableTag.asText().split(":")).toList();
+
+ AddressTag addressTag = new AddressTag();
+ addressTag.setKind(Integer.valueOf(list.getFirst()));
+ addressTag.setPublicKey(new PublicKey(list.get(1)));
+ addressTag.setIdentifierTag(new IdentifierTag(list.get(2)));
+
+ return addressTag;
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ String.format("Malformed JsonNode addressable tag: [%s]", addressableTag.asText()), e);
+ }
+ }
+
+ @Override
+ public String getFilterableValue() {
+ Integer kind = addressableTag.getKind();
+ String hexString = addressableTag.getPublicKey().toHexString();
+ String id = addressableTag.getIdentifierTag().getId();
+
+ return Stream.of(kind, hexString, id)
+ .map(Object::toString)
+ .collect(Collectors.joining(":"));
+ }
+
+ private boolean compare(@NonNull GenericEvent genericEvent) {
+ return
+ !genericEvent.getPubKey().toHexString().equals(
+ this.addressableTag.getPublicKey().toHexString()) ||
+ !genericEvent.getKind().equals(
+ this.addressableTag.getKind()) ||
+ getTypeSpecificTags(IdentifierTag.class, genericEvent).stream()
+ .anyMatch(identifierTag ->
+ identifierTag.getId().equals(
+ this.addressableTag.getIdentifierTag().getId()));
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java
new file mode 100644
index 000000000..dfd0d185a
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java
@@ -0,0 +1,38 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import nostr.base.PublicKey;
+import nostr.event.impl.GenericEvent;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class AuthorFilter implements Filterable {
+ public final static String filterKey = "authors";
+ private final T publicKey;
+
+ public AuthorFilter(T publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ this.publicKey.toHexString().equals(genericEvent.getPubKey().toHexString());
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return publicKey;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return publicKey.toHexString();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java
new file mode 100644
index 000000000..c6fae8a50
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java
@@ -0,0 +1,37 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import nostr.event.impl.GenericEvent;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class EventFilter implements Filterable {
+ public final static String filterKey = "ids";
+ private final T event;
+
+ public EventFilter(T event) {
+ this.event = event;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ this.event.getId().equals(genericEvent.getId());
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return event;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return event.getId();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java
new file mode 100644
index 000000000..002ded0ba
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java
@@ -0,0 +1,45 @@
+package nostr.event.filter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import nostr.event.BaseTag;
+import nostr.event.impl.GenericEvent;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+public interface Filterable {
+ ObjectMapper mapper = new ObjectMapper();
+
+ Predicate getPredicate();
+ T getFilterCriterion();
+ Object getFilterableValue();
+ String getFilterKey();
+
+ default List getTypeSpecificTags(Class tagClass, GenericEvent event) {
+ return event.getTags().stream()
+ .filter(tagClass::isInstance)
+ .map(tagClass::cast)
+ .toList();
+ }
+
+ default ObjectNode toObjectNode(ObjectNode objectNode) {
+ ArrayNode arrayNode = mapper.createArrayNode();
+
+ Optional.ofNullable(objectNode.get(getFilterKey()))
+ .ifPresent(jsonNode ->
+ jsonNode.elements().forEachRemaining(arrayNode::add));
+
+ addToArrayNode(arrayNode);
+
+ return objectNode.set(getFilterKey(), arrayNode);
+ }
+
+ default void addToArrayNode(ArrayNode arrayNode) {
+ arrayNode.addAll(
+ mapper.createArrayNode().add(
+ getFilterableValue().toString()));
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java
new file mode 100644
index 000000000..65e1a12e5
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java
@@ -0,0 +1,57 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static java.util.stream.Collectors.groupingBy;
+
+@EqualsAndHashCode
+public class Filters {
+ public static final int DEFAULT_FILTERS_LIMIT = 10;
+ @Getter
+ private final Map> filtersMap;
+
+ @Getter
+ @Setter
+ private Integer limit = DEFAULT_FILTERS_LIMIT;
+
+ public Filters(@NonNull Filterable... filterablesByDefaultType) {
+ this(List.of(filterablesByDefaultType));
+ }
+
+ public Filters(@NonNull List filterablesByDefaultType) {
+ this(filterablesByDefaultType.stream().collect(groupingBy(Filterable::getFilterKey)));
+ }
+
+ private Filters(@NonNull Map> filterablesByCustomType) {
+ validateFiltersMap(filterablesByCustomType);
+ this.filtersMap = filterablesByCustomType;
+ }
+
+ public List getFilterByType(@NonNull String type) {
+ return filtersMap.get(type);
+ }
+
+ private static void validateFiltersMap(Map> filtersMap) throws IllegalArgumentException {
+ if (filtersMap.isEmpty()) {
+ throw new IllegalArgumentException("Filters cannot be empty.");
+ }
+
+ filtersMap.values().forEach(filterables -> {
+ if (filterables.isEmpty()) {
+ throw new IllegalArgumentException("Filters cannot be empty.");
+ }
+ });
+
+ filtersMap.forEach((key, value) -> {
+ if (key.isEmpty())
+ throw new IllegalArgumentException(String.format("Filter key for filterable [%s] is not defined", value.getFirst().getFilterKey()));
+ });
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java
new file mode 100644
index 000000000..324ba6557
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java
@@ -0,0 +1,48 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import nostr.base.ElementAttribute;
+import nostr.base.GenericTagQuery;
+import nostr.event.impl.GenericEvent;
+import nostr.event.impl.GenericTag;
+
+import java.util.HashSet;
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class GenericTagQueryFilter implements Filterable {
+ private final T genericTagQuery;
+
+ public GenericTagQueryFilter(T genericTagQuery) {
+ this.genericTagQuery = genericTagQuery;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ getTypeSpecificTags(GenericTag.class, genericEvent).stream()
+ .filter(genericTag ->
+ genericTag.getCode().equals(this.genericTagQuery.getTagName()))
+ .anyMatch(genericTag ->
+ new HashSet<>(genericTag
+ .getAttributes().stream().map(
+ ElementAttribute::getValue).toList())
+ .contains(
+ this.genericTagQuery.getValue()));
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return genericTagQuery;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return genericTagQuery.getTagName();
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return genericTagQuery.getValue();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java
new file mode 100644
index 000000000..6efc2b1ff
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java
@@ -0,0 +1,39 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import nostr.event.impl.GenericEvent;
+import nostr.event.tag.IdentifierTag;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class IdentifierTagFilter implements Filterable {
+ public final static String filterKey = "#d";
+ private final T identifierTag;
+
+ public IdentifierTagFilter(T identifierTag) {
+ this.identifierTag = identifierTag;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ getTypeSpecificTags(IdentifierTag.class, genericEvent).stream().anyMatch(genericEventIdentifiterTag ->
+ genericEventIdentifiterTag.getId().equals(this.identifierTag.getId()));
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return identifierTag;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return identifierTag.getId();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java
new file mode 100644
index 000000000..d018a913d
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java
@@ -0,0 +1,46 @@
+package nostr.event.filter;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import lombok.EqualsAndHashCode;
+import nostr.event.Kind;
+import nostr.event.impl.GenericEvent;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class KindFilter implements Filterable {
+ public final static String filterKey = "kinds";
+ private final T kind;
+
+ public KindFilter(T kind) {
+ this.kind = kind;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ genericEvent.getKind().equals(this.kind.getValue());
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return kind;
+ }
+
+ @Override
+ public void addToArrayNode(ArrayNode arrayNode) {
+ arrayNode.addAll(
+ mapper.createArrayNode().add(
+ getFilterableValue()));
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public Integer getFilterableValue() {
+ return kind.getValue();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java
new file mode 100644
index 000000000..4ac62b0db
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java
@@ -0,0 +1,40 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import nostr.event.impl.GenericEvent;
+import nostr.event.tag.EventTag;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class ReferencedEventFilter implements Filterable {
+ public final static String filterKey = "#e";
+ private final T referencedEvent;
+
+ public ReferencedEventFilter(T referencedEvent) {
+ this.referencedEvent = referencedEvent;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ getTypeSpecificTags(EventTag.class, genericEvent).stream()
+ .anyMatch(eventTag ->
+ eventTag.getIdEvent().equals(referencedEvent.getId()));
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return referencedEvent;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return referencedEvent.getId();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java
new file mode 100644
index 000000000..4dc4eab03
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java
@@ -0,0 +1,41 @@
+package nostr.event.filter;
+
+import lombok.EqualsAndHashCode;
+import nostr.base.PublicKey;
+import nostr.event.impl.GenericEvent;
+import nostr.event.tag.PubKeyTag;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class ReferencedPublicKeyFilter implements Filterable {
+ public final static String filterKey = "#p";
+ private final T referencedPublicKey;
+
+ public ReferencedPublicKeyFilter(T referencedPublicKey) {
+ this.referencedPublicKey = referencedPublicKey;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ getTypeSpecificTags(PubKeyTag.class, genericEvent).stream()
+ .anyMatch(pubKeyTag ->
+ pubKeyTag.getPublicKey().toHexString().equals(this.referencedPublicKey.toHexString()));
+ }
+
+ @Override
+ public T getFilterCriterion() {
+ return referencedPublicKey;
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return referencedPublicKey.toHexString();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java
new file mode 100644
index 000000000..fb06f049e
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java
@@ -0,0 +1,43 @@
+package nostr.event.filter;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.EqualsAndHashCode;
+import nostr.event.impl.GenericEvent;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class SinceFilter implements Filterable {
+ public final static String filterKey = "since";
+ private final Long since;
+
+ public SinceFilter(Long since) {
+ this.since = since;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ this.since < genericEvent.getCreatedAt();
+ }
+
+ @Override
+ public Long getFilterCriterion() {
+ return since;
+ }
+
+ @Override
+ public ObjectNode toObjectNode(ObjectNode objectNode) {
+ return mapper.createObjectNode().put(filterKey, since);
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return since.toString();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java
new file mode 100644
index 000000000..54c9e2a99
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java
@@ -0,0 +1,43 @@
+package nostr.event.filter;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.EqualsAndHashCode;
+import nostr.event.impl.GenericEvent;
+
+import java.util.function.Predicate;
+
+@EqualsAndHashCode
+public class UntilFilter implements Filterable {
+ public final static String filterKey = "until";
+ private final Long until;
+
+ public UntilFilter(Long until) {
+ this.until = until;
+ }
+
+ @Override
+ public Predicate getPredicate() {
+ return (genericEvent) ->
+ this.until >= genericEvent.getCreatedAt();
+ }
+
+ @Override
+ public Long getFilterCriterion() {
+ return until;
+ }
+
+ @Override
+ public ObjectNode toObjectNode(ObjectNode objectNode) {
+ return mapper.createObjectNode().put(filterKey, until);
+ }
+
+ @Override
+ public String getFilterKey() {
+ return filterKey;
+ }
+
+ @Override
+ public String getFilterableValue() {
+ return until.toString();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java
new file mode 100644
index 000000000..0c0145a3b
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java
@@ -0,0 +1,42 @@
+package nostr.event.impl;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+import nostr.event.AbstractEventContent;
+import nostr.event.impl.CalendarContent.CalendarContentBuilder;
+import nostr.event.impl.CalendarRsvpContent.CalendarRsvpContentBuilder;
+import nostr.event.tag.AddressTag;
+import nostr.event.tag.GeohashTag;
+import nostr.event.tag.HashtagTag;
+import nostr.event.tag.IdentifierTag;
+import nostr.event.tag.PubKeyTag;
+import nostr.event.tag.ReferenceTag;
+
+import java.util.List;
+
+@Data
+@Builder
+@JsonDeserialize(builder = CalendarRsvpContentBuilder.class)
+@EqualsAndHashCode(callSuper = false)
+public class CalendarRsvpContent extends AbstractEventContent {
+ //@JsonProperty
+ private final String id;
+
+ // below fields mandatory
+ private final IdentifierTag identifierTag;
+ private final AddressTag addressTag;
+ private final String status;
+
+ // below fields optional
+ private List participantPubKeys;
+
+ public static CalendarRsvpContentBuilder builder(@NonNull IdentifierTag identifierTag, @NonNull AddressTag addressTag, @NonNull String status) {
+ return new CalendarRsvpContentBuilder()
+ .identifierTag(identifierTag)
+ .addressTag(addressTag)
+ .status(status);
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java
new file mode 100644
index 000000000..6243aa523
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java
@@ -0,0 +1,118 @@
+package nostr.event.impl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NonNull;
+import nostr.base.PublicKey;
+import nostr.base.Signature;
+import nostr.base.annotation.Event;
+import nostr.event.BaseTag;
+import nostr.event.Kind;
+import nostr.event.NIP52Event;
+import nostr.event.impl.CalendarRsvpEvent.CalendarRsvpEventDeserializer;
+import nostr.event.impl.CalendarTimeBasedEvent.CalendarTimeBasedEventDeserializer;
+import nostr.event.tag.AddressTag;
+import nostr.event.tag.IdentifierTag;
+import nostr.event.tag.PubKeyTag;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.StreamSupport;
+
+@EqualsAndHashCode(callSuper = false)
+@Event(name = "CalendarRsvpEvent", nip = 52)
+@JsonDeserialize(using = CalendarRsvpEventDeserializer.class)
+public class CalendarRsvpEvent extends NIP52Event {
+ @Getter
+ @JsonIgnore
+ private CalendarRsvpContent calendarRsvpContent;
+
+ public CalendarRsvpEvent(@NonNull PublicKey sender, @NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) {
+ super(sender, Kind.CALENDAR_RSVP_EVENT, baseTags, content);
+ this.calendarRsvpContent = calendarRsvpContent;
+ mapCustomTags();
+ }
+
+ private void mapCustomTags() {
+ addStandardTag(calendarRsvpContent.getIdentifierTag());
+ addStandardTag(calendarRsvpContent.getAddressTag());
+ addGenericTag("status", getNip(), calendarRsvpContent.getStatus());
+ addStandardTag(calendarRsvpContent.getParticipantPubKeys());
+ }
+
+ public static class CalendarRsvpEventDeserializer extends StdDeserializer {
+ public CalendarRsvpEventDeserializer() {
+ super(CalendarRsvpEvent.class);
+ }
+
+// TODO: below methods needs comprehensive tags assignment completion
+ @Override
+ public CalendarRsvpEvent deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
+ JsonNode calendarTimeBasedEventNode = jsonParser.getCodec().readTree(jsonParser);
+ ArrayNode tags = (ArrayNode) calendarTimeBasedEventNode.get("tags");
+
+ List baseTags = StreamSupport.stream(
+ tags.spliterator(), false).toList().stream()
+ .map(
+ JsonNode::elements)
+ .map(element ->
+ new ObjectMapper().convertValue(element, BaseTag.class)).toList();
+
+ List genericTags = baseTags.stream()
+ .filter(GenericTag.class::isInstance)
+ .map(GenericTag.class::cast)
+ .toList();
+
+ IdentifierTag identifierTag = getBaseTagCastFromString(baseTags, IdentifierTag.class).getFirst();
+
+ AddressTag addressTag = getBaseTagCastFromString(baseTags, AddressTag.class).getFirst();
+
+ CalendarRsvpContent calendarRsvpContent = CalendarRsvpContent.builder(
+ identifierTag,
+ addressTag,
+ getTagValueFromString(genericTags, "status"))
+ .build();
+
+ calendarRsvpContent.setParticipantPubKeys(getBaseTagCastFromString(baseTags, PubKeyTag.class));
+
+ Map generalMap = new HashMap<>();
+ calendarTimeBasedEventNode.fields().forEachRemaining(generalTag ->
+ generalMap.put(
+ generalTag.getKey(),
+ generalTag.getValue().asText()));
+
+
+ CalendarRsvpEvent calendarTimeBasedEvent = new CalendarRsvpEvent(
+ new PublicKey(generalMap.get("pubkey")),
+ baseTags,
+ generalMap.get("content"),
+ calendarRsvpContent
+ );
+ calendarTimeBasedEvent.setId(generalMap.get("id"));
+ calendarTimeBasedEvent.setCreatedAt(Long.valueOf(generalMap.get("created_at")));
+ calendarTimeBasedEvent.setSignature(Signature.fromString(generalMap.get("sig")));
+
+ return calendarTimeBasedEvent;
+ }
+
+ private String getTagValueFromString(List genericTags, String code) {
+ return genericTags.stream()
+ .filter(tag -> tag.getCode().equalsIgnoreCase(code))
+ .findFirst().get().getAttributes().get(0).getValue().toString();
+ }
+
+ private List getBaseTagCastFromString(List baseTags, Class type) {
+ return baseTags.stream().filter(type::isInstance).map(type::cast).toList();
+ }
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java
index 752211272..3ab5f9319 100644
--- a/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java
+++ b/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java
@@ -15,6 +15,7 @@
import nostr.base.annotation.Event;
import nostr.event.AbstractEventContent;
import nostr.event.BaseTag;
+import nostr.event.Kind;
/**
*
@@ -26,7 +27,7 @@
public class CreateOrUpdateStallEvent extends NostrMarketplaceEvent {
public CreateOrUpdateStallEvent(PublicKey sender, List tags, @NonNull Stall stall) {
- super(sender, 30017, tags, stall);
+ super(sender, Kind.STALL_CREATE_OR_UPDATE.getValue(), tags, stall);
}
@Getter
diff --git a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java
deleted file mode 100644
index 4e987ab32..000000000
--- a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package nostr.event.impl;
-
-import com.fasterxml.jackson.annotation.JsonAnyGetter;
-import com.fasterxml.jackson.annotation.JsonAnySetter;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import lombok.Setter;
-import nostr.base.PublicKey;
-import nostr.base.annotation.Key;
-import nostr.event.Kind;
-import nostr.event.json.serializer.CustomIdEventListSerializer;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-/**
- *
- * @author squirrel
- */
-@Builder
-@Data
-@EqualsAndHashCode(callSuper = false)
-@NoArgsConstructor
-@AllArgsConstructor
-public class Filters {
-
- @Key
- @JsonProperty("ids")
- @JsonSerialize(using=CustomIdEventListSerializer.class)
- private List events;
-
- @Key
- @JsonProperty("authors")
- private List authors;
-
- @Key
- private List kinds;
-
- @Key
- @JsonProperty("#e")
- @JsonSerialize(using=CustomIdEventListSerializer.class)
- private List referencedEvents;
-
- @Key
- @JsonProperty("#p")
- private List referencePubKeys;
-
- @Key
- private Long since;
-
- @Key
- private Long until;
-
- @Key
- private Integer limit;
-
- @Key(nip = 12)
- @Setter(AccessLevel.NONE)
- private Map> genericTagQuery;
-
- public void setUntil(@NonNull Long until) {
- if (until < 0) {
- throw new IllegalArgumentException("'until' filter cannot be negative.");
- }
- this.until = until;
- }
-
- public void setSince(@NonNull Long since) {
- if (since < 0) {
- throw new IllegalArgumentException("'since' filter cannot be negative.");
- }
- this.since = since;
- }
-
- @JsonAnyGetter
- public Map> getGenericTagQuery() {
- return genericTagQuery;
- }
-
- @JsonAnySetter
- public void setGenericTagQuery(String key, List value) {
- this.genericTagQuery = Optional.ofNullable(genericTagQuery).orElse(new HashMap<>());
- this.genericTagQuery.put(key, value);
- }
-}
diff --git a/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java
index d53764347..e31c423e6 100644
--- a/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java
+++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java
@@ -124,7 +124,7 @@ public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull List
public GenericEvent(@NonNull PublicKey pubKey, @NonNull Integer kind, @NonNull List tags,
@NonNull String content) {
this.pubKey = pubKey;
- this.kind = kind;
+ this.kind = Kind.valueOf(kind).getValue();
this.tags = tags;
this.content = content;
this.attributes = new ArrayList<>();
diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java
index 40dadbbb7..385d7f461 100644
--- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java
+++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java
@@ -1,9 +1,10 @@
package nostr.event.json.codec;
import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.NonNull;
-import lombok.SneakyThrows;
import nostr.base.IDecoder;
import nostr.event.BaseMessage;
import nostr.event.impl.GenericMessage;
@@ -16,37 +17,66 @@
import nostr.event.message.RelayAuthenticationMessage;
import nostr.event.message.ReqMessage;
+import java.util.List;
import java.util.Map;
/**
* @author eric
*/
public class BaseMessageDecoder implements IDecoder {
- private final ObjectMapper mapper;
+ public static final int MAX_JSON_NODE_THRESHOLD = 99;
+ private final ObjectMapper mapper = new ObjectMapper();
public BaseMessageDecoder() {
- mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
}
- @SneakyThrows
@Override
- public T decode(@NonNull String jsonString) {
- Object[] msgArr = mapper.readValue(jsonString, Object[].class);
- final String strCmd = msgArr[0].toString();
- final Object arg = msgArr[1];
+ public T decode(@NonNull String jsonString) throws JsonProcessingException {
+ ValidJsonNodeFirstPair validJsonNodeFirstPair = jsonFirstPair(jsonString);
+ String command = validJsonNodeFirstPair.formerly_strCmd();
+ Object subscriptionId = validJsonNodeFirstPair.formerly_arg(); // subscriptionId
- return switch (strCmd) {
- case "AUTH" -> arg instanceof Map map ?
+ Object[] msgArr = mapper.readValue(jsonString, Object[].class); // TODO: replace with jsonNode after ReqMessage.decode() is finished
+
+ return switch (command) {
+ case "AUTH" -> subscriptionId instanceof Map map ?
CanonicalAuthenticationMessage.decode(map, mapper) :
- RelayAuthenticationMessage.decode(arg);
- case "CLOSE" -> CloseMessage.decode(arg);
- case "EOSE" -> EoseMessage.decode(arg);
+ RelayAuthenticationMessage.decode(subscriptionId);
+ case "CLOSE" -> CloseMessage.decode(subscriptionId);
+ case "EOSE" -> EoseMessage.decode(subscriptionId);
case "EVENT" -> EventMessage.decode(msgArr, mapper);
- case "NOTICE" -> NoticeMessage.decode(arg);
+ case "NOTICE" -> NoticeMessage.decode(subscriptionId);
case "OK" -> OkMessage.decode(msgArr);
- case "REQ" -> ReqMessage.decode(msgArr, mapper);
+ case "REQ" -> {
+ String filtersJson = jsonSecondPair(jsonString).formerly_msgArr(); // filters
+ yield ReqMessage.decode(subscriptionId, List.of(filtersJson));
+ }
default -> GenericMessage.decode(msgArr);
};
}
+
+ private ValidJsonNodeFirstPair jsonFirstPair(@NonNull String jsonString) throws JsonProcessingException {
+ final JsonNode jsonNode = mapper.readTree(jsonString);
+
+ return new ValidJsonNodeFirstPair(
+ jsonNode.get(0).asText(),
+ jsonNode.get(1).asText());
+ }
+
+ private ValidJsonNodeSecondPair jsonSecondPair(@NonNull String jsonString) throws JsonProcessingException {
+ final JsonNode jsonNode = mapper.readTree(jsonString);
+
+ return new ValidJsonNodeSecondPair(
+ jsonNode.get(2).toString());
+ }
+
+ private record ValidJsonNodeFirstPair(
+ @NonNull String formerly_strCmd,
+ @NonNull Object formerly_arg) {
+ }
+
+ private record ValidJsonNodeSecondPair(
+ @NonNull String formerly_msgArr) {
+ }
}
diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FilterableProvider.java
new file mode 100644
index 000000000..507d24f6c
--- /dev/null
+++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FilterableProvider.java
@@ -0,0 +1,45 @@
+package nostr.event.json.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import nostr.base.GenericTagQuery;
+import nostr.base.PublicKey;
+import nostr.event.Kind;
+import nostr.event.filter.AddressableTagFilter;
+import nostr.event.filter.AuthorFilter;
+import nostr.event.filter.EventFilter;
+import nostr.event.filter.Filterable;
+import nostr.event.filter.GenericTagQueryFilter;
+import nostr.event.filter.IdentifierTagFilter;
+import nostr.event.filter.KindFilter;
+import nostr.event.filter.ReferencedEventFilter;
+import nostr.event.filter.ReferencedPublicKeyFilter;
+import nostr.event.filter.SinceFilter;
+import nostr.event.filter.UntilFilter;
+import nostr.event.impl.GenericEvent;
+import nostr.event.tag.IdentifierTag;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.StreamSupport;
+
+class FilterableProvider {
+ protected static List getFilterable(String type, JsonNode node) {
+ return switch (type) {
+ case ReferencedPublicKeyFilter.filterKey -> getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText())));
+ case ReferencedEventFilter.filterKey -> getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText())));
+ case AddressableTagFilter.filterKey -> getFilterable(node, addressableTag -> new AddressableTagFilter<>(AddressableTagFilter.createAddressTag(addressableTag)));
+ case IdentifierTagFilter.filterKey -> getFilterable(node, identifierTag -> new IdentifierTagFilter<>(new IdentifierTag(identifierTag.asText())));
+ case AuthorFilter.filterKey -> getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText())));
+ case EventFilter.filterKey -> getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText())));
+ case KindFilter.filterKey -> getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt())));
+ case SinceFilter.filterKey -> List.of(new SinceFilter(node.asLong()));
+ case UntilFilter.filterKey -> List.of(new UntilFilter(node.asLong()));
+ default ->
+ getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText())));
+ };
+ }
+
+ private static List getFilterable(JsonNode jsonNode, Function filterFunction) {
+ return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList();
+ }
+}
diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java
index b957d1c00..71659c7c5 100644
--- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java
+++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java
@@ -1,26 +1,32 @@
package nostr.event.json.codec;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.NonNull;
-import nostr.event.impl.Filters;
+import lombok.SneakyThrows;
+import nostr.event.filter.Filterable;
+import nostr.event.filter.Filters;
+
+import java.util.ArrayList;
+import java.util.List;
/**
- *
* @author eric
*/
@Data
public class FiltersDecoder implements FDecoder {
- private final Class clazz = (Class)Filters.class;
+ private final static ObjectMapper mapper = new ObjectMapper();
+
+ @SneakyThrows
+ public T decode(@NonNull String jsonFiltersList) {
+ final List filterables = new ArrayList<>();
+
+ mapper.readTree(jsonFiltersList).fields().forEachRemaining(field ->
+ filterables.addAll(
+ FilterableProvider.getFilterable(
+ field.getKey(),
+ field.getValue())));
- @Override
- public T decode(@NonNull String jsonString) {
- try {
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(jsonString, clazz);
- } catch (JsonProcessingException ex) {
- throw new RuntimeException(ex);
- }
- }
+ return (T) new Filters(filterables);
+ }
}
diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java
index 73f620606..5675cd752 100644
--- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java
+++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java
@@ -1,22 +1,11 @@
package nostr.event.json.codec;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import nostr.base.FEncoder;
-import nostr.event.impl.Filters;
-import nostr.util.NostrException;
+import nostr.event.filter.Filters;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.stream.StreamSupport;
-
-/**
- * @author guilhermegps
- */
@Data
@EqualsAndHashCode(callSuper = false)
public class FiltersEncoder implements FEncoder {
@@ -26,45 +15,21 @@ public FiltersEncoder(Filters filters) {
this.filters = filters;
}
- protected String toJson() throws NostrException {
- try {
- JsonNode node = MAPPER.valueToTree(filters);
- ObjectNode objNode = (ObjectNode) node;
- //var arrayNode = (ArrayNode) node.get("genericTagQuery");
- if (objNode != null && !objNode.isNull()) {
- for (JsonNode jn : objNode) {
- StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(jn.fields(), Spliterator.ORDERED), false)
- .forEach(f -> {
- if ("genericTagQuery".equals(f.getKey())) {
- var mapper = new ObjectMapper();
- try {
- mapper.readTree(f.getValue().toString())
- .fields()
- .forEachRemaining(g -> objNode.set(g.getKey(), g.getValue()));
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- } else {
- objNode.set(f.getKey(), f.getValue());
- }
- });
- }
- }
- objNode.remove("genericTagQuery");
-
- return MAPPER.writeValueAsString(objNode);
- } catch (JsonProcessingException | IllegalArgumentException e) {
- throw new NostrException(e);
- }
- }
-
@Override
public String encode() {
- try {
- return toJson();
- } catch (NostrException ex) {
- throw new RuntimeException(ex);
- }
+ ObjectNode root = MAPPER.createObjectNode();
+
+ filters.getFiltersMap().forEach((key, filterableList) -> {
+ final ObjectNode objectNode = MAPPER.createObjectNode();
+ root.setAll(
+ filterableList
+ .stream()
+ .map(filterable ->
+ filterable.toObjectNode(objectNode))
+ .toList()
+ .getFirst());
+ });
+
+ return root.toString();
}
}
diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java
deleted file mode 100644
index 8a43f46d2..000000000
--- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package nostr.event.json.codec;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import lombok.Data;
-import nostr.base.FEncoder;
-import nostr.event.impl.Filters;
-import nostr.util.NostrException;
-
-import java.util.List;
-
-@Data
-public class FiltersListEncoder implements FEncoder {
-
- private final List filtersList;
-
- public FiltersListEncoder(List filtersList) {
- this.filtersList = filtersList;
- }
-
- @Override
- public String encode() {
- try {
- return toJson();
- } catch (NostrException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- protected String toJson() throws NostrException {
- return toJsonCommaSeparated();
- }
-
- private String toJsonArray() throws NostrException {
- try {
- StringBuilder sb = new StringBuilder();
- for (Object filter : getFiltersList()) {
- if (!sb.isEmpty()) {
- sb.append(",");
- }
- sb.append(MAPPER.writeValueAsString(filter));
- }
- return sb.toString();
- } catch (JsonProcessingException | IllegalArgumentException e) {
- throw new NostrException(e);
- }
- }
-
- private String toJsonCommaSeparated() throws NostrException {
- JsonNode node = MAPPER.valueToTree(getFiltersList());
- try {
- return MAPPER.writeValueAsString(node);
- } catch (JsonProcessingException e) {
- throw new NostrException(e);
- }
-
- }
-}
diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java
index b97ffeca4..533d61e19 100644
--- a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java
+++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java
@@ -25,13 +25,9 @@ public GenericEventDecoder(Class clazz) {
}
@Override
- public T decode(String jsonEvent) {
- try {
- var mapper = new ObjectMapper();
- mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
- return mapper.readValue(jsonEvent, clazz);
- } catch (JsonProcessingException ex) {
- throw new RuntimeException(ex);
- }
+ public T decode(String jsonEvent) throws JsonProcessingException {
+ var mapper = new ObjectMapper();
+ mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+ return mapper.readValue(jsonEvent, clazz);
}
}
diff --git a/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java b/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java
index 9c81a7fd2..7caf29d7d 100644
--- a/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java
+++ b/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java
@@ -51,15 +51,25 @@ public String encode() throws JsonProcessingException {
public static T decode(@NonNull Object[] msgArr, ObjectMapper mapper) {
var arg = msgArr[1];
if (msgArr.length == 2 && arg instanceof Map map) {
- var event = mapper.convertValue(map, new TypeReference() {});
- return (T) new EventMessage(event);
- } else if (msgArr.length == 3 && arg instanceof String) {
- var subId = arg.toString();
+ return (T) new EventMessage(
+ convertValue(mapper, map)
+ );
+ }
+
+ if (msgArr.length == 3 && arg instanceof String) {
if (msgArr[2] instanceof Map map) {
- var event = mapper.convertValue(map, new TypeReference() {});
- return (T) new EventMessage(event, subId);
+ return (T) new EventMessage(
+ convertValue(mapper, map),
+ arg.toString()
+ );
}
}
+
throw new AssertionError("Invalid argument: " + arg);
}
+
+ private static GenericEvent convertValue(ObjectMapper mapper, Map map) {
+ return mapper.convertValue(map, new TypeReference() {
+ });
+ }
}
diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java
index 17fadf42d..7e6a9c547 100644
--- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java
+++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java
@@ -2,8 +2,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
@@ -11,66 +10,74 @@
import nostr.base.Command;
import nostr.base.IEncoder;
import nostr.event.BaseMessage;
-import nostr.event.impl.Filters;
+import nostr.event.filter.Filters;
+import nostr.event.json.codec.FiltersDecoder;
import nostr.event.json.codec.FiltersEncoder;
import java.time.temporal.ValueRange;
-import java.util.ArrayList;
import java.util.List;
/**
- *
* @author squirrel
*/
@Getter
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class ReqMessage extends BaseMessage {
-
@JsonProperty
private final String subscriptionId;
@JsonProperty
private final List filtersList;
- public ReqMessage(@NonNull String subscriptionId, Filters filters) {
- this(subscriptionId, List.of(filters));
+ public ReqMessage(@NonNull String subscriptionId, Filters... filtersList) {
+// TODO: complete logic for a list of filters
+ this(subscriptionId, List.of(filtersList));
}
- public ReqMessage(@NonNull String subscriptionId, List incomingFiltersList) {
+ public ReqMessage(@NonNull String subscriptionId, List filtersList) {
super(Command.REQ.name());
- if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) {
- throw new IllegalArgumentException(String.format("subscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length()));
- }
+ validateSubscriptionId(subscriptionId);
this.subscriptionId = subscriptionId;
- this.filtersList = new ArrayList<>();
- this.filtersList.addAll(incomingFiltersList);
+ this.filtersList = filtersList;
}
@Override
public String encode() throws JsonProcessingException {
getArrayNode()
- .add(getCommand())
- .add(getSubscriptionId());
- List localFiltersList = getFiltersList();
- for (Filters f : localFiltersList) {
- try {
- FiltersEncoder filtersEncoder = new FiltersEncoder(f);
- var filterNode = IEncoder.MAPPER.readTree(filtersEncoder.encode());
- getArrayNode().add(filterNode);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ .add(getCommand())
+ .add(getSubscriptionId());
+
+ filtersList.stream()
+ .map(FiltersEncoder::new)
+ .map(FiltersEncoder::encode)
+ .map(ReqMessage::createJsonNode)
+ .forEach(jsonNode ->
+ getArrayNode().add(jsonNode));
+
return IEncoder.MAPPER.writeValueAsString(getArrayNode());
}
- public static T decode(@NonNull Object[] msgArr, ObjectMapper mapper) {
- var len = msgArr.length - 2;
- var filtersArr = new Object[len];
- System.arraycopy(msgArr, 2, filtersArr, 0, len);
- var filtersList = mapper.convertValue(filtersArr, new TypeReference>() {
- });
- return (T) new ReqMessage(msgArr[1].toString(), filtersList);
+ public static T decode(@NonNull Object subscriptionId, @NonNull List jsonFiltersList) {
+ validateSubscriptionId(subscriptionId.toString());
+ ReqMessage reqMessage = new ReqMessage(
+ subscriptionId.toString(),
+ jsonFiltersList.stream().map(filtersList ->
+ new FiltersDecoder<>().decode(filtersList)).toList());
+ return (T) reqMessage;
+ }
+
+ private static void validateSubscriptionId(String subscriptionId) {
+ if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) {
+ throw new IllegalArgumentException(String.format("SubscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length()));
+ }
+ }
+
+ private static JsonNode createJsonNode(String jsonNode) {
+ try {
+ return IEncoder.MAPPER.readTree(jsonNode);
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException(String.format("Malformed encoding ReqMessage json: [%s]", jsonNode), e);
+ }
}
}
diff --git a/nostr-java-examples/pom.xml b/nostr-java-examples/pom.xml
index a3a9b808f..ad1dfcf4d 100644
--- a/nostr-java-examples/pom.xml
+++ b/nostr-java-examples/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-examples
diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java
index 0a3117e6a..b2316ca45 100644
--- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java
+++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java
@@ -15,12 +15,15 @@
import nostr.event.BaseTag;
import nostr.event.Kind;
import nostr.event.Reaction;
+import nostr.event.filter.AuthorFilter;
+import nostr.event.filter.Filters;
+import nostr.event.filter.KindFilter;
+import nostr.event.filter.SinceFilter;
import nostr.event.impl.ChannelCreateEvent;
import nostr.event.impl.ChannelMessageEvent;
import nostr.event.impl.DeletionEvent;
import nostr.event.impl.DirectMessageEvent;
import nostr.event.impl.EphemeralEvent;
-import nostr.event.impl.Filters;
import nostr.event.impl.GenericEvent;
import nostr.event.impl.HideMessageEvent;
import nostr.event.impl.InternetIdentifierMetadataEvent;
@@ -243,7 +246,7 @@ private static void ephemerealEvent() {
logHeader("ephemeralEvent");
var nip01 = new NIP01(SENDER);
- nip01.createEphemeralEvent(21000, "An ephemeral event")
+ nip01.createEphemeralEvent(Kind.EPHEMEREAL_EVENT.getValue(), "An ephemeral event")
.sign()
.send(RELAYS);
}
@@ -260,7 +263,7 @@ private static void reactionEvent() {
var reactionEvent = nip25.createReactionEvent(event.getEvent(), Reaction.LIKE);
reactionEvent.signAndSend(RELAYS);
nip25.createReactionEvent(event.getEvent(), "💩").signAndSend();
-// Using Custom Emoji as reaction
+// Using Custom Emoji as reaction
nip25.createReactionEvent(
event.getEvent(),
NIP30.createCustomEmojiTag(
@@ -276,7 +279,7 @@ private static void replaceableEvent() {
var event = nip01.createTextNoteEvent("Hello Astral, Please replace me!");
event.signAndSend(RELAYS);
- nip01.createReplaceableEvent(List.of(new EventTag(event.getEvent().getId())), 15_000, "New content").signAndSend();
+ nip01.createReplaceableEvent(List.of(new EventTag(event.getEvent().getId())), Kind.REPLACEABLE_EVENT.getValue(), "New content").signAndSend();
}
private static void internetIdMetadata() {
@@ -296,20 +299,18 @@ private static void internetIdMetadata() {
public static void filters() throws InterruptedException {
logHeader("filters");
- var kinds = List.of(Kind.EPHEMEREAL_EVENT, Kind.TEXT_NOTE);
- var authors = new ArrayList<>(List.of(new PublicKey("21ef0d8541375ae4bca85285097fba370f7e540b5a30e5e75670c16679f9d144")));
-
var date = Calendar.getInstance();
var subId = "subId" + date.getTimeInMillis();
date.add(Calendar.DAY_OF_MONTH, -5);
- Filters filters = Filters.builder()
- .authors(authors)
- .kinds(kinds)
- .since(date.getTimeInMillis()/1000)
- .build();
var nip01 = NIP01.getInstance();
- nip01.setRelays(RELAYS).send(filters, subId);
+ nip01.setRelays(RELAYS).sendRequest(
+ new Filters(
+ new KindFilter<>(Kind.EPHEMEREAL_EVENT),
+ new KindFilter<>(Kind.TEXT_NOTE),
+ new AuthorFilter<>(new PublicKey("21ef0d8541375ae4bca85285097fba370f7e540b5a30e5e75670c16679f9d144")),
+ new SinceFilter(date.getTimeInMillis()/1000)), subId);
+
Thread.sleep(5000);
}
diff --git a/nostr-java-id/pom.xml b/nostr-java-id/pom.xml
index f31c7bd08..3b9796f47 100644
--- a/nostr-java-id/pom.xml
+++ b/nostr-java-id/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-id
diff --git a/nostr-java-test/pom.xml b/nostr-java-test/pom.xml
index 15e049822..25ff07bd8 100644
--- a/nostr-java-test/pom.xml
+++ b/nostr-java-test/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
nostr-java
- 0.6.3-SNAPSHOT
+ 0.6.4-SNAPSHOT
nostr-java-test
@@ -58,13 +58,6 @@
${project.version}
test
-
- ${project.groupId}
- nostr-java-command-provider
- ${project.version}
- test
-
-
org.springframework
@@ -193,4 +186,4 @@
-
\ No newline at end of file
+
diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java
index 67cfca4d6..6afb38149 100644
--- a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java
+++ b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java
@@ -9,9 +9,9 @@
import nostr.event.BaseTag;
import nostr.event.Kind;
import nostr.event.Reaction;
+import nostr.event.filter.Filters;
import nostr.event.impl.DirectMessageEvent;
import nostr.event.impl.EphemeralEvent;
-import nostr.event.impl.Filters;
import nostr.event.impl.GenericEvent;
import nostr.event.impl.GenericTag;
import nostr.event.impl.InternetIdentifierMetadataEvent;
@@ -29,7 +29,6 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.Random;
/**
@@ -59,10 +58,6 @@ public static DirectMessageEvent createDirectMessageEvent(PublicKey senderPublic
return event;
}
- public static Filters createFilters(List authors, List kindList, Long since) {
- return Filters.builder().authors(authors).kinds(kindList).since(since).build();
- }
-
public static InternetIdentifierMetadataEvent createInternetIdentifierMetadataEvent(UserProfile profile) {
final PublicKey publicKey = profile.getPublicKey();
InternetIdentifierMetadataEvent event = new InternetIdentifierMetadataEvent(publicKey, profile);
@@ -139,26 +134,7 @@ public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Int
return tag;
}
- public static Filters createFilters(PublicKey publicKey) {
- List eventList = new ArrayList<>();
- eventList.add(createTextNoteEvent(publicKey));
- eventList.add(createEphemeralEvent(publicKey));
-
- List refEvents = new ArrayList<>();
- refEvents.add(createTextNoteEvent(publicKey));
-
- GenericTagQuery genericTagQuery = createGenericTagQuery();
- return Filters.builder()
- .events(eventList)
- .referencedEvents(refEvents)
- .genericTagQuery(
- Map.of(
- genericTagQuery.getTagName(),
- genericTagQuery.getValue()))
- .build();
- }
-
- public static GenericTagQuery createGenericTagQuery() {
+ public static List createGenericTagQuery() {
Character c = generateRamdomAlpha(1).charAt(0);
String v1 = generateRamdomAlpha(5);
String v2 = generateRamdomAlpha(6);
@@ -169,10 +145,12 @@ public static GenericTagQuery createGenericTagQuery() {
list.add(v2);
list.add(v1);
- var result = new GenericTagQuery();
- result.setTagName(c.toString());
- result.setValue(list);
- return result;
+ return list.stream().map(item -> {
+ var result = new GenericTagQuery();
+ result.setTagName(c.toString());
+ result.setValue(item);
+ return result;
+ }).toList();
}
}
diff --git a/nostr-java-test/src/test/java/nostr/test/client/ClientTest.java b/nostr-java-test/src/test/java/nostr/test/client/ClientTest.java
deleted file mode 100644
index 3fdc5a491..000000000
--- a/nostr-java-test/src/test/java/nostr/test/client/ClientTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package nostr.test.client;
-
-import lombok.extern.java.Log;
-import nostr.base.PrivateKey;
-import nostr.client.Client;
-import nostr.context.impl.DefaultRequestContext;
-import nostr.event.BaseMessage;
-import nostr.event.message.EventMessage;
-import nostr.id.Identity;
-import nostr.test.EntityFactory;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.TimeoutException;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * @author squirrel
- */
-@Log
-class ClientTest {
-
- private Client client;
- private Identity identity;
-
- public ClientTest() {
- }
-
- @BeforeEach
- public void init() {
- log.info("init");
- identity = Identity.create(PrivateKey.generateRandomPrivKey());
-
- DefaultRequestContext requestContext = new DefaultRequestContext();
- requestContext.setPrivateKey(identity.getPrivateKey().getRawData());
- //requestContext.setRelays(Map.of("My local test relay", "ws://localhost:5555"));
- Properties loadedProperties = getRelayProperties();
- requestContext.setRelays(convertPropertiesToMap(loadedProperties));
- try {
- client = Client.getInstance().connect(requestContext);
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
- }
- }
-
- //@AfterEach
- public void dispose() {
- log.info("dispose");
- try {
- this.client.disconnect();
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
- }
- this.client = null;
- this.identity = null;
- }
-
- @Test
- public void testSend() {
- log.info("testSend");
- var event = EntityFactory.Events.createTextNoteEvent(identity.getPublicKey());
- identity.sign(event);
- BaseMessage msg = new EventMessage(event);
- assertDoesNotThrow(() -> {
- client.send(msg);
- });
- assertTrue(true);
- }
-
- @Test
- public void disconnect() {
- log.info("disconnect");
-
- int relayCount = getRelayCount();
- assertEquals(relayCount, client.getOpenConnectionsCount());
- assertDoesNotThrow(() -> {
- client.disconnect();
- });
- assertEquals(0, client.getOpenConnectionsCount());
-
- assertDoesNotThrow(() -> {
- client.disconnect(); // if all connections are closed, trying to disconnect again wont throw error
- });
- }
-
-/*
- @Test
- public void testNip42() {
- System.out.println("testNip42");
-
- var rcpt = Identity.generateRandomIdentity().getPublicKey();
- var sender = identity.getPublicKey();
- var event = EntityFactory.Events.createDirectMessageEvent(sender, rcpt, "Hello, World!");
- identity.sign(event);
- BaseMessage msg = new EventMessage(event);
- assertDoesNotThrow(() -> {
- client.send(msg);
-
- var filters = Filters.builder().kinds(new KindList(4)).authors(new PublicKeyList(sender)).build();
- msg = new ReqMessage("testNip42_" + sender.toString(), filters);
- client.send(msg);
- });
- }
-*/
-
- private Properties getRelayProperties() {
- Properties properties = new Properties();
- try (InputStream is = getClass().getClassLoader().getResourceAsStream("relays.properties")) {
- if (is != null) {
- properties.load(is);
- } else {
- throw new IOException("Cannot find relays.properties on the classpath.");
- }
- } catch (IOException e) {
- log.severe(e.getMessage());
- }
- return properties;
- }
-
- private int getRelayCount() {
- return getRelayProperties().size();
- }
-
- public static Map convertPropertiesToMap(Properties properties) {
- Map map = new HashMap<>();
-
- // Iterate over the properties and put each entry into the map
- Set propertyNames = properties.stringPropertyNames();
- for (String name : propertyNames) {
- map.put(name, properties.getProperty(name));
- }
-
- return map;
- }
-
-}
diff --git a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java
index 0c11089a6..f44849bbf 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java
@@ -3,11 +3,12 @@
import nostr.api.NIP01;
import nostr.api.NIP09;
import nostr.base.Relay;
-import nostr.client.springwebsocket.SpringWebSocketClient;
import nostr.event.BaseMessage;
import nostr.event.BaseTag;
import nostr.event.Kind;
-import nostr.event.impl.Filters;
+import nostr.event.filter.AuthorFilter;
+import nostr.event.filter.Filters;
+import nostr.event.filter.KindFilter;
import nostr.event.impl.GenericEvent;
import nostr.event.impl.ReplaceableEvent;
import nostr.event.impl.TextNoteEvent;
@@ -32,11 +33,6 @@
public class APINIP09EventTest {
private static final String RELAY_URI = "ws://localhost:5555";
- private final SpringWebSocketClient springWebSocketClient;
-
- public APINIP09EventTest() {
- springWebSocketClient = new SpringWebSocketClient(RELAY_URI);
- }
@Test
public void deleteEvent() throws IOException {
@@ -47,21 +43,18 @@ public void deleteEvent() throws IOException {
NIP01 nip01 = new NIP01<>(identity);
nip01.createTextNoteEvent("Delete me!").signAndSend(Map.of("local", RELAY_URI));
+ Filters filters = new Filters(
+ new KindFilter<>(Kind.TEXT_NOTE),
+ new AuthorFilter<>(identity.getPublicKey()));
- Filters filters = Filters
- .builder()
- .kinds(List.of(Kind.TEXT_NOTE))
- .authors(List.of(identity.getPublicKey()))
- .build();
-
- List result = nip01.send(filters, UUID.randomUUID().toString());
+ List result = nip01.sendRequest(filters, UUID.randomUUID().toString());
assertFalse(result.isEmpty());
assertEquals(2, result.size());
nip09.createDeletionEvent(nip01.getEvent()).signAndSend(Map.of("local", RELAY_URI));
- result = nip01.send(filters, UUID.randomUUID().toString());
+ result = nip01.sendRequest(filters, UUID.randomUUID().toString());
assertFalse(result.isEmpty());
assertEquals(1, result.size());
diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java
index 26a5a6b35..26e88c8a1 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java
@@ -17,13 +17,11 @@
import nostr.crypto.bech32.Bech32;
import nostr.crypto.bech32.Bech32Prefix;
import nostr.event.BaseTag;
-import nostr.event.Kind;
import nostr.event.impl.CalendarContent;
import nostr.event.impl.CreateOrUpdateStallEvent;
import nostr.event.impl.CreateOrUpdateStallEvent.Stall;
import nostr.event.impl.DirectMessageEvent;
import nostr.event.impl.EncryptedPayloadEvent;
-import nostr.event.impl.Filters;
import nostr.event.impl.NostrMarketplaceEvent;
import nostr.event.impl.NostrMarketplaceEvent.Product.Spec;
import nostr.event.impl.TextNoteEvent;
@@ -43,7 +41,6 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java
index 8e78c7e19..6baba40fa 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java
@@ -1,17 +1,6 @@
package nostr.test.event;
-import java.io.IOException;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.jupiter.api.Order;
-import org.junit.jupiter.api.Test;
-
import com.fasterxml.jackson.databind.ObjectMapper;
-
import nostr.api.NIP52;
import nostr.base.PublicKey;
import nostr.client.springwebsocket.SpringWebSocketClient;
@@ -19,9 +8,6 @@
import nostr.event.impl.CalendarContent;
import nostr.event.impl.GenericEvent;
import nostr.event.impl.GenericTag;
-import nostr.event.json.codec.BaseEventEncoder;
-import nostr.event.json.codec.BaseMessageDecoder;
-import nostr.event.json.codec.GenericEventDecoder;
import nostr.event.message.EventMessage;
import nostr.event.tag.EventTag;
import nostr.event.tag.GeohashTag;
@@ -30,7 +16,15 @@
import nostr.event.tag.PubKeyTag;
import nostr.event.tag.ReferenceTag;
import nostr.id.Identity;
-import nostr.test.util.JsonComparator;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
class ApiNIP52RequestTest {
private static final String PRV_KEY_VALUE = "23c011c4c02de9aa98d48c3646c70bb0e7ae30bdae1dfed4d251cbceadaeeb7b";
@@ -103,9 +97,9 @@ void testNIP99CalendarContentPreRequest() throws IOException {
tags.add(R_TAG);
CalendarContent calendarContent = CalendarContent.builder(
- new IdentifierTag(UUID_CALENDAR_TIME_BASED_EVENT_TEST),
- TITLE,
- Long.valueOf(START))
+ new IdentifierTag(UUID_CALENDAR_TIME_BASED_EVENT_TEST),
+ TITLE,
+ Long.valueOf(START))
.build();
var nip52 = new NIP52<>(Identity.create(PRV_KEY_VALUE));
@@ -152,18 +146,6 @@ void testNIP99CalendarContentPreRequest() throws IOException {
*/
}
- private T mapJsonToEvent(List reqResponse, Class clazz) {
- Optional first = reqResponse
- .stream()
- .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage))
- .map(eventMessage -> ((GenericEvent) eventMessage.getEvent()))
- .map(event -> new BaseEventEncoder<>(event).encode())
- .map(encode -> new GenericEventDecoder<>(clazz).decode(encode))
- .findFirst();
- T t = first.get();
- return t;
- }
-
private String expectedEventResponseJson(String subscriptionId) {
return "[\"OK\",\"" + subscriptionId + "\",true,\"success: request processed\"]";
}
diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java
index 26e8881c9..10b9017b0 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java
@@ -1,18 +1,7 @@
package nostr.test.event;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.junit.jupiter.api.Order;
-import org.junit.jupiter.api.Test;
-
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-
import nostr.api.NIP99;
import nostr.base.PublicKey;
import nostr.client.springwebsocket.SpringWebSocketClient;
@@ -20,9 +9,6 @@
import nostr.event.impl.ClassifiedListing;
import nostr.event.impl.GenericEvent;
import nostr.event.impl.GenericTag;
-import nostr.event.json.codec.BaseEventEncoder;
-import nostr.event.json.codec.BaseMessageDecoder;
-import nostr.event.json.codec.GenericEventDecoder;
import nostr.event.message.EventMessage;
import nostr.event.tag.EventTag;
import nostr.event.tag.GeohashTag;
@@ -31,8 +17,17 @@
import nostr.event.tag.PubKeyTag;
import nostr.event.tag.SubjectTag;
import nostr.id.Identity;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
import static nostr.test.event.ClassifiedListingEventTest.LOCATION_CODE;
import static nostr.test.event.ClassifiedListingEventTest.PUBLISHED_AT_CODE;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class ApiNIP99RequestTest {
private static final String PRV_KEY_VALUE = "23c011c4c02de9aa98d48c3646c70bb0e7ae30bdae1dfed4d251cbceadaeeb7b";
@@ -85,9 +80,9 @@ void testNIP99ClassifiedListingPreRequest() throws IOException {
PriceTag priceTag = new PriceTag(NUMBER, CURRENCY, FREQUENCY);
ClassifiedListing classifiedListing = ClassifiedListing.builder(
- TITLE,
- SUMMARY,
- priceTag)
+ TITLE,
+ SUMMARY,
+ priceTag)
.build();
var nip99 = new NIP99<>(Identity.create(PRV_KEY_VALUE));
@@ -148,18 +143,6 @@ void testNIP99ClassifiedListingPreRequest() throws IOException {
*/
}
- private T mapJsonToEvent(List reqResponse, Class clazz) {
- Optional first = reqResponse
- .stream()
- .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage))
- .map(eventMessage -> ((GenericEvent) eventMessage.getEvent()))
- .map(event -> new BaseEventEncoder<>(event).encode())
- .map(encode -> new GenericEventDecoder<>(clazz).decode(encode))
- .findFirst();
- T t = first.get();
- return t;
- }
-
private String expectedEventResponseJson(String subscriptionId) {
return "[\"OK\",\"" + subscriptionId + "\",true,\"success: request processed\"]";
}
diff --git a/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java b/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java
new file mode 100644
index 000000000..94000b102
--- /dev/null
+++ b/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java
@@ -0,0 +1,72 @@
+package nostr.test.event;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import lombok.extern.java.Log;
+import nostr.event.BaseMessage;
+import nostr.event.json.codec.BaseMessageDecoder;
+import nostr.event.message.EoseMessage;
+import nostr.event.message.ReqMessage;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@Log
+public class BaseMessageCommandMapperTest {
+// TODO: flesh out remaining commands
+ public final static String REQ_JSON =
+ "[\"REQ\", " +
+ "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " +
+ "{\"kinds\": [1], " +
+ "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," +
+ "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]";
+
+ @Test
+ public void testReqMessageDecoder() throws JsonProcessingException {
+ log.info("testReqMessageDecoder");
+
+ BaseMessage decode = new BaseMessageDecoder<>().decode(REQ_JSON);
+ assertInstanceOf(ReqMessage.class, decode);
+ }
+
+ @Test
+ public void testReqMessageDecoderType() {
+ log.info("testReqMessageDecoderType");
+
+ assertDoesNotThrow(() -> {
+ new BaseMessageDecoder().decode(REQ_JSON);
+ });
+
+ assertDoesNotThrow(() -> {
+ ReqMessage reqMessage = new BaseMessageDecoder().decode(REQ_JSON);
+ });
+ }
+
+ @Test
+ public void testReqMessageDecoderThrows() {
+ log.info("testReqMessageDecoderThrows");
+
+ assertThrows(ClassCastException.class, () -> {
+ EoseMessage decode = new BaseMessageDecoder().decode(REQ_JSON);
+ });
+ }
+
+ @Test
+ public void testReqMessageDecoderDoesNotThrow() {
+ log.info("testReqMessageDecoderDoesNotThrow");
+
+ assertDoesNotThrow(() -> {
+ new BaseMessageDecoder().decode(REQ_JSON);
+ });
+ }
+
+ @Test
+ public void testReqMessageDecoderThrows3() {
+ log.info("testReqMessageDecoderThrows");
+
+ assertThrows(ClassCastException.class, () -> {
+ EoseMessage decode = new BaseMessageDecoder().decode(REQ_JSON);
+ });
+ }
+}
diff --git a/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java b/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java
index b375a85cd..7e2dae22b 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java
@@ -82,25 +82,19 @@ public class CalendarContentDecodeTest {
@Test
void testCalendarContentMinimalJsonDecoding() {
- assertDoesNotThrow(() -> {
- CalendarTimeBasedEvent decode = new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventMinimalJson);
- return decode;
- });
+ assertDoesNotThrow(() ->
+ new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventMinimalJson));
}
@Test
void testCalendarContentFullJsonDecoding() {
- assertDoesNotThrow(() -> {
- CalendarTimeBasedEvent decode = new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson);
- return decode;
- });
+ assertDoesNotThrow(() ->
+ new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson));
}
@Test
void testCalendarContentProblemBarchettaJsonDecoding() {
- assertDoesNotThrow(() -> {
- CalendarTimeBasedEvent decoded = new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson);
- return decoded;
- });
+ assertDoesNotThrow(() ->
+ new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson));
}
}
diff --git a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java
index b75ecdd49..1850e4d01 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java
@@ -1,84 +1,90 @@
-package nostr.test.event;
-
-import nostr.base.PublicKey;
-import nostr.event.BaseMessage;
-import nostr.event.BaseTag;
-import nostr.event.Marker;
-import nostr.event.impl.GenericEvent;
-import nostr.event.json.codec.BaseMessageDecoder;
-import nostr.event.message.EventMessage;
-import nostr.event.tag.EventTag;
-import nostr.event.tag.PubKeyTag;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DecodeTest {
-
- @Test
- public void decodeTest() {
-
- String json = "["
- + "\"EVENT\","
- + "\"temp20230627\","
- + "{"
- + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\","
- + "\"kind\":1,"
- + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\","
- + "\"created_at\":1687765220,"
- + "\"content\":\"手順書が間違ってたら作業者は無理だな\","
- + "\"tags\":["
- + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"],"
- + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]"
- + "],"
- + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\""
- + "}]";
-
- BaseMessage message = new BaseMessageDecoder<>().decode(json);
-
- Assertions.assertEquals("EVENT", message.getCommand());
- EventMessage eventMessage = (EventMessage) message;
-
- Assertions.assertEquals("temp20230627", eventMessage.getSubscriptionId());
- GenericEvent eventImpl = (GenericEvent) eventMessage.getEvent();
-
- Assertions.assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", eventImpl.getId());
- Assertions.assertEquals(1, eventImpl.getKind());
- Assertions.assertEquals("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984", eventImpl.getPubKey().toString());
- Assertions.assertEquals(1687765220, eventImpl.getCreatedAt());
- Assertions.assertEquals("手順書が間違ってたら作業者は無理だな", eventImpl.getContent());
- Assertions.assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546",
- eventImpl.getSignature().toString());
-
- List expectedTags = new ArrayList<>();
- EventTag eventTag = new EventTag("494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346");
- eventTag.setRecommendedRelayUrl("");
- eventTag.setMarker(Marker.ROOT);
- expectedTags.add(eventTag);
- PubKeyTag pubKeyTag = new PubKeyTag();
- pubKeyTag.setPublicKey(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984"));
- expectedTags.add(pubKeyTag);
-
- List extends BaseTag> actualTags = eventImpl.getTags();
-
- for (int i = 0; i < expectedTags.size(); i++) {
- BaseTag expected = expectedTags.get(i);
- if (expected instanceof EventTag expetedEventTag) {
- EventTag actualEventTag = (EventTag) actualTags.get(i);
- Assertions.assertEquals(expetedEventTag.getIdEvent(), actualEventTag.getIdEvent());
- Assertions.assertEquals(expetedEventTag.getRecommendedRelayUrl(), actualEventTag.getRecommendedRelayUrl());
- Assertions.assertEquals(expetedEventTag.getMarker(), actualEventTag.getMarker());
- } else if (expected instanceof PubKeyTag expectedPublicKeyTag) {
- PubKeyTag actualPublicKeyTag = (PubKeyTag) actualTags.get(i);
- Assertions.assertEquals(expectedPublicKeyTag.getPublicKey().toString(), actualPublicKeyTag.getPublicKey().toString());
- } else {
- Assertions.fail();
- }
-
- }
-
- }
-
-}
+package nostr.test.event;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import nostr.base.PublicKey;
+import nostr.event.BaseMessage;
+import nostr.event.BaseTag;
+import nostr.event.Marker;
+import nostr.event.impl.GenericEvent;
+import nostr.event.json.codec.BaseMessageDecoder;
+import nostr.event.message.EventMessage;
+import nostr.event.tag.EventTag;
+import nostr.event.tag.PubKeyTag;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class DecodeTest {
+
+ @Test
+ public void decodeTest() throws JsonProcessingException {
+
+ String json = "["
+ + "\"EVENT\","
+ + "\"temp20230627\","
+ + "{"
+ + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\","
+ + "\"kind\":1,"
+ + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\","
+ + "\"created_at\":1687765220,"
+ + "\"content\":\"手順書が間違ってたら作業者は無理だな\","
+ + "\"tags\":["
+ + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"],"
+ + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]"
+ + "],"
+ + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\""
+ + "}]";
+
+ BaseMessage message = new BaseMessageDecoder<>().decode(json);
+
+ assertEquals("EVENT", message.getCommand());
+ assertInstanceOf(EventMessage.class, message);
+
+ EventMessage eventMessage = (EventMessage) message;
+
+ assertEquals("temp20230627", eventMessage.getSubscriptionId());
+ GenericEvent eventImpl = (GenericEvent) eventMessage.getEvent();
+
+ assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", eventImpl.getId());
+ assertEquals(1, eventImpl.getKind());
+ assertEquals("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984", eventImpl.getPubKey().toString());
+ assertEquals(1687765220, eventImpl.getCreatedAt());
+ assertEquals("手順書が間違ってたら作業者は無理だな", eventImpl.getContent());
+ assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546",
+ eventImpl.getSignature().toString());
+
+ List expectedTags = new ArrayList<>();
+ EventTag eventTag = new EventTag("494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346");
+ eventTag.setRecommendedRelayUrl("");
+ eventTag.setMarker(Marker.ROOT);
+ expectedTags.add(eventTag);
+ PubKeyTag pubKeyTag = new PubKeyTag();
+ pubKeyTag.setPublicKey(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984"));
+ expectedTags.add(pubKeyTag);
+
+ List extends BaseTag> actualTags = eventImpl.getTags();
+
+ for (int i = 0; i < expectedTags.size(); i++) {
+ BaseTag expected = expectedTags.get(i);
+ if (expected instanceof EventTag expetedEventTag) {
+ EventTag actualEventTag = (EventTag) actualTags.get(i);
+ assertEquals(expetedEventTag.getIdEvent(), actualEventTag.getIdEvent());
+ assertEquals(expetedEventTag.getRecommendedRelayUrl(), actualEventTag.getRecommendedRelayUrl());
+ assertEquals(expetedEventTag.getMarker(), actualEventTag.getMarker());
+ } else if (expected instanceof PubKeyTag expectedPublicKeyTag) {
+ PubKeyTag actualPublicKeyTag = (PubKeyTag) actualTags.get(i);
+ assertEquals(expectedPublicKeyTag.getPublicKey().toString(), actualPublicKeyTag.getPublicKey().toString());
+ } else {
+ fail();
+ }
+
+ }
+
+ }
+
+}
diff --git a/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java b/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java
index 48b1f5283..cbb4b44b4 100644
--- a/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java
@@ -4,6 +4,7 @@
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
public class KindMappingTest {
@Test
@@ -15,4 +16,9 @@ void testKindValueOf() {
void testKindName() {
assertEquals("text_note", Kind.valueOf(1).getName());
}
+
+ @Test
+ void testKindUndefinedName() {
+ assertThrows(IllegalArgumentException.class, () -> Kind.valueOf(9999999));
+ }
}
diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java
new file mode 100644
index 000000000..85c8bdeca
--- /dev/null
+++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java
@@ -0,0 +1,324 @@
+package nostr.test.filters;
+
+import lombok.extern.java.Log;
+import nostr.base.GenericTagQuery;
+import nostr.base.PublicKey;
+import nostr.event.Kind;
+import nostr.event.filter.AddressableTagFilter;
+import nostr.event.filter.EventFilter;
+import nostr.event.filter.Filters;
+import nostr.event.filter.GenericTagQueryFilter;
+import nostr.event.filter.IdentifierTagFilter;
+import nostr.event.filter.KindFilter;
+import nostr.event.filter.ReferencedEventFilter;
+import nostr.event.filter.ReferencedPublicKeyFilter;
+import nostr.event.filter.SinceFilter;
+import nostr.event.filter.UntilFilter;
+import nostr.event.impl.GenericEvent;
+import nostr.event.json.codec.FiltersDecoder;
+import nostr.event.tag.AddressTag;
+import nostr.event.tag.IdentifierTag;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@Log
+public class FiltersDecoderTest {
+
+ @Test
+ public void testEventFiltersDecoder() {
+ log.info("testEventFiltersDecoder");
+
+ String filterKey = "ids";
+ String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ String expected = "{\"" + filterKey + "\":[\"" + eventId + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new EventFilter<>(new GenericEvent(eventId))),
+ decodedFilters);
+ }
+
+ @Test
+ public void testMultipleEventFiltersDecoder() {
+ log.info("testMultipleEventFiltersDecoder");
+
+ String filterKey = "ids";
+ String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String eventId2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+
+ String joined = String.join("\",\"", eventId1, eventId2);
+
+ String expected = "{\"" + filterKey + "\":[\"" + joined + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new EventFilter<>(new GenericEvent(eventId1)),
+ new EventFilter<>(new GenericEvent(eventId2))),
+ decodedFilters);
+ }
+
+
+ @Test
+ public void testAddressableTagFiltersDecoder() {
+ log.info("testAddressableTagFiltersDecoder");
+
+ Integer kind = 1;
+ String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String uuidValue1 = "UUID-1";
+
+ String joined = String.join(":", String.valueOf(kind), author, uuidValue1);
+
+ AddressTag addressTag = new AddressTag();
+ addressTag.setKind(kind);
+ addressTag.setPublicKey(new PublicKey(author));
+ addressTag.setIdentifierTag(new IdentifierTag(uuidValue1));
+
+ String expected = "{\"#a\":[\"" + joined + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new AddressableTagFilter<>(addressTag)),
+ decodedFilters);
+ }
+
+ @Test
+ public void testMultipleAddressableTagFiltersDecoder() {
+ log.info("testMultipleAddressableTagFiltersDecoder");
+
+ Integer kind1 = 1;
+ String author1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String uuidValue1 = "UUID-1";
+
+ Integer kind2 = 1;
+ String author2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ String uuidValue2 = "UUID-2";
+
+ AddressTag addressTag1 = new AddressTag();
+ addressTag1.setKind(kind1);
+ addressTag1.setPublicKey(new PublicKey(author1));
+ addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1));
+
+ AddressTag addressTag2 = new AddressTag();
+ addressTag2.setKind(kind2);
+ addressTag2.setPublicKey(new PublicKey(author2));
+ addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2));
+
+ String joined1 = String.join(":", String.valueOf(kind1), author1, uuidValue1);
+ String joined2 = String.join(":", String.valueOf(kind2), author2, uuidValue2);
+
+ String joined3 = String.join("\",\"", joined1, joined2);
+
+ String expected = "{\"#a\":[\"" + joined3 + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new AddressableTagFilter<>(addressTag1),
+ new AddressableTagFilter<>(addressTag2)),
+ decodedFilters);
+ }
+
+ @Test
+ public void testKindFiltersDecoder() {
+ log.info("testKindFiltersDecoder");
+
+ String filterKey = KindFilter.filterKey;
+ Kind kind = Kind.valueOf(1);
+
+ String expected = "{\"" + filterKey + "\":[" + kind.toString() + "]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(new Filters(new KindFilter<>(kind)), decodedFilters);
+ }
+
+ @Test
+ public void testMultipleKindFiltersDecoder() {
+ log.info("testMultipleKindFiltersDecoder");
+
+ String filterKey = KindFilter.filterKey;
+ Kind kind1 = Kind.valueOf(1);
+ Kind kind2 = Kind.valueOf(2);
+
+ String join = String.join(",", kind1.toString(), kind2.toString());
+
+ String expected = "{\"" + filterKey + "\":[" + join + "]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new KindFilter<>(kind1),
+ new KindFilter<>(kind2)),
+ decodedFilters);
+ }
+
+ @Test
+ public void testIdentifierTagFilterDecoder() {
+ log.info("testIdentifierTagFilterDecoder");
+
+ String uuidValue1 = "UUID-1";
+
+ String expected = "{\"#d\":[\"" + uuidValue1 + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+
+ assertEquals(new Filters(new IdentifierTagFilter<>(new IdentifierTag(uuidValue1))), decodedFilters);
+ }
+
+ @Test
+ public void testMultipleIdentifierTagFilterDecoder() {
+ log.info("testMultipleIdentifierTagFilterDecoder");
+
+ String uuidValue1 = "UUID-1";
+ String uuidValue2 = "UUID-2";
+
+ String joined = String.join("\",\"", uuidValue1, uuidValue2);
+ String expected = "{\"#d\":[\"" + joined + "\"]}";
+
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)),
+ new IdentifierTagFilter<>(new IdentifierTag(uuidValue2))),
+ decodedFilters);
+ }
+
+ @Test
+ public void testReferencedEventFilterDecoder() {
+ log.info("testReferencedEventFilterDecoder");
+
+ String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ String expected = "{\"#e\":[\"" + eventId + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(new Filters(new ReferencedEventFilter<>(new GenericEvent(eventId))), decodedFilters);
+ }
+
+ @Test
+ public void testMultipleReferencedEventFilterDecoder() {
+ log.info("testMultipleReferencedEventFilterDecoder");
+
+ String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String eventId2 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ String joined = String.join("\",\"", eventId1, eventId2);
+ String expected = "{\"#e\":[\"" + joined + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new ReferencedEventFilter<>(new GenericEvent(eventId1)),
+ new ReferencedEventFilter<>(new GenericEvent(eventId2))),
+ decodedFilters);
+ }
+
+ @Test
+ public void testReferencedPublicKeyFilterDecoder() {
+ log.info("testReferencedPublicKeyFilterDecoder");
+
+ String pubkeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ String expected = "{\"#p\":[\"" + pubkeyString + "\"]}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(new Filters(new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString))), decodedFilters);
+ }
+
+ @Test
+ public void testMultipleReferencedPublicKeyFilterDecoder() {
+ log.info("testMultipleReferencedPublicKeyFilterDecoder");
+
+ String pubkeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String pubkeyString2 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ String joined = String.join("\",\"", pubkeyString1, pubkeyString2);
+ String expected = "{\"#p\":[\"" + joined + "\"]}";
+
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(
+ new Filters(
+ new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString1)),
+ new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString2))),
+ decodedFilters);
+ }
+
+ @Test
+ public void testGenericTagFiltersDecoder() {
+ log.info("testGenericTagFiltersDecoder");
+
+ String geohashKey = "#g";
+ String geohashValue = "2vghde";
+ String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}";
+
+ Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode);
+
+ assertEquals(new Filters(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue))), decodedFilters);
+ }
+
+ @Test
+ public void testMultipleGenericTagFiltersDecoder() {
+ log.info("testMultipleGenericTagFiltersDecoder");
+
+ String geohashKey = "#g";
+ String geohashValue1 = "2vghde";
+ String geohashValue2 = "3abcde";
+ String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}";
+
+ Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode);
+
+ assertEquals(
+ new Filters(
+ new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)),
+ new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2))),
+ decodedFilters);
+ }
+
+ @Test
+ public void testSinceFiltersDecoder() {
+ log.info("testSinceFiltersDecoder");
+
+ Long since = Date.from(Instant.now()).getTime();
+
+ String expected = "{\"since\":" + since + "}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(new Filters(new SinceFilter(since)), decodedFilters);
+ }
+
+ @Test
+ public void testUntilFiltersDecoder() {
+ log.info("testUntilFiltersDecoder");
+
+ Long until = Date.from(Instant.now()).getTime();
+
+ String expected = "{\"until\":" + until + "}";
+ Filters decodedFilters = new FiltersDecoder<>().decode(expected);
+
+ assertEquals(new Filters(new UntilFilter(until)), decodedFilters);
+ }
+
+ @Test
+ public void testFailedAddressableTagMalformedSeparator() {
+ log.info("testFailedAddressableTagMalformedSeparator");
+
+ Integer kind = 1;
+ String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String uuidValue1 = "UUID-1";
+
+ String malformedJoin = String.join(",", String.valueOf(kind), author, uuidValue1);
+ String expected = "{\"#a\":[\"" + malformedJoin + "\"]}";
+
+ assertThrows(IllegalArgumentException.class, () -> new FiltersDecoder<>().decode(expected));
+ }
+}
diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java
new file mode 100644
index 000000000..3331c8106
--- /dev/null
+++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java
@@ -0,0 +1,335 @@
+package nostr.test.filters;
+
+import lombok.extern.java.Log;
+import nostr.base.GenericTagQuery;
+import nostr.base.PublicKey;
+import nostr.event.Kind;
+import nostr.event.filter.AddressableTagFilter;
+import nostr.event.filter.AuthorFilter;
+import nostr.event.filter.EventFilter;
+import nostr.event.filter.Filterable;
+import nostr.event.filter.Filters;
+import nostr.event.filter.GenericTagQueryFilter;
+import nostr.event.filter.IdentifierTagFilter;
+import nostr.event.filter.KindFilter;
+import nostr.event.filter.ReferencedEventFilter;
+import nostr.event.filter.ReferencedPublicKeyFilter;
+import nostr.event.filter.SinceFilter;
+import nostr.event.filter.UntilFilter;
+import nostr.event.impl.GenericEvent;
+import nostr.event.json.codec.FiltersEncoder;
+import nostr.event.message.ReqMessage;
+import nostr.event.tag.AddressTag;
+import nostr.event.tag.IdentifierTag;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@Log
+public class FiltersEncoderTest {
+
+ @Test
+ public void testEventFilterEncoderUsingVarArgs() {
+ log.info("testEventFilterEncoderUsingVarArgs");
+
+ String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new EventFilter<>(new GenericEvent(eventId))));
+
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testEventFilterEncoderUsingList() {
+ log.info("testEventFilterEncoderUsingList");
+
+ List expectedFilters = new ArrayList<>();
+
+ String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ expectedFilters.add(new EventFilter<>(new GenericEvent(eventId)));
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters));
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testEventFilterEncoderByMap() {
+ log.info("testEventFilterEncoderByMap");
+
+ String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new EventFilter<>(new GenericEvent(eventId))));
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testKindFiltersEncoder() {
+ log.info("testKindFiltersEncoder");
+
+ Kind kind = Kind.valueOf(1);
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new KindFilter<>(kind)));
+
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"kinds\":[" + kind.toString() + "]}", encodedFilters);
+ }
+
+ @Test
+ public void testAuthorFilterEncoder() {
+ log.info("testAuthorFilterEncoder");
+
+ String pubKeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new AuthorFilter<>(new PublicKey(pubKeyString))));
+
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"authors\":[\"" + pubKeyString + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleAuthorFilterEncoder() {
+ log.info("testMultipleAuthorFilterEncoder");
+
+ String pubKeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String pubKeyString2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ List.of(
+ new AuthorFilter<>(new PublicKey(pubKeyString1)),
+ new AuthorFilter<>(new PublicKey(pubKeyString2)))));
+
+ String encodedFilters = encoder.encode();
+ String authorPubKeys = String.join("\",\"", pubKeyString1, pubKeyString2);
+
+ assertEquals("{\"authors\":[\"" + authorPubKeys + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleKindFiltersEncoder() {
+ log.info("testMultipleKindFiltersEncoder");
+
+ Kind kind1 = Kind.valueOf(1);
+ Kind kind2 = Kind.valueOf(2);
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ List.of(
+ new KindFilter<>(kind1),
+ new KindFilter<>(kind2))));
+
+ String encodedFilters = encoder.encode();
+ String kinds = String.join(",", kind1.toString(), kind2.toString());
+ assertEquals("{\"kinds\":[" + kinds + "]}", encodedFilters);
+ }
+
+ @Test
+ public void testAddressableTagFilterEncoder() {
+ log.info("testAddressableTagFilterEncoder");
+
+ Integer kind = 1;
+ String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String uuidValue1 = "UUID-1";
+
+ AddressTag addressTag = new AddressTag();
+ addressTag.setKind(kind);
+ addressTag.setPublicKey(new PublicKey(author));
+ addressTag.setIdentifierTag(new IdentifierTag(uuidValue1));
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new AddressableTagFilter<>(addressTag)));
+ String encodedFilters = encoder.encode();
+ String addressableTag = String.join(":", String.valueOf(kind), author, uuidValue1);
+
+ assertEquals("{\"#a\":[\"" + addressableTag + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testIdentifierTagFilterEncoder() {
+ log.info("testIdentifierTagFilterEncoder");
+
+ String uuidValue1 = "UUID-1";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new IdentifierTagFilter<>(new IdentifierTag(uuidValue1))));
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"#d\":[\"" + uuidValue1 + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleIdentifierTagFilterEncoder() {
+ log.info("testMultipleIdentifierTagFilterEncoder");
+
+ String uuidValue1 = "UUID-1";
+ String uuidValue2 = "UUID-2";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ List.of(
+ new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)),
+ new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))));
+
+ String encodedFilters = encoder.encode();
+ String dTags = String.join("\",\"", uuidValue1, uuidValue2);
+ assertEquals("{\"#d\":[\"" + dTags + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testReferencedEventFilterEncoder() {
+ log.info("testReferencedEventFilterEncoder");
+
+ String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new ReferencedEventFilter<>(new GenericEvent(eventId))));
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"#e\":[\"" + eventId + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleReferencedEventFilterEncoder() {
+ log.info("testMultipleReferencedEventFilterEncoder");
+
+ String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String eventId2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ List.of(
+ new ReferencedEventFilter<>(new GenericEvent(eventId1)),
+ new ReferencedEventFilter<>(new GenericEvent(eventId2)))));
+
+ String encodedFilters = encoder.encode();
+ String eventIds = String.join("\",\"", eventId1, eventId2);
+ assertEquals("{\"#e\":[\"" + eventIds + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testSingleGenericTagQueryFiltersEncoder() {
+ log.info("testSingleGenericTagQueryFiltersEncoder");
+
+ String geohashKey = "#g";
+ String new_geohash = "2vghde";
+
+ FiltersEncoder encoder = new FiltersEncoder(
+ new Filters(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, new_geohash))));
+
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"#g\":[\"2vghde\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testReferencedPublicKeyFilterEncoder() {
+ log.info("testReferencedPublicKeyFilterEncoder");
+
+ String pubKeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString))));
+
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"#p\":[\"" + pubKeyString + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleReferencedPublicKeyFilterEncoder() {
+ log.info("testMultipleReferencedPublicKeyFilterEncoder");
+
+ String pubKeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String pubKeyString2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString1)),
+ new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString2))));
+
+ String encodedFilters = encoder.encode();
+ String pubKeyTags = String.join("\",\"", pubKeyString1, pubKeyString2);
+ assertEquals("{\"#p\":[\"" + pubKeyTags + "\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleGenericTagQueryFiltersEncoder() {
+ log.info("testMultipleGenericTagQueryFiltersEncoder");
+
+ String geohashKey = "#g";
+ String geohashValue1 = "2vghde";
+ String geohashValue2 = "3abcde";
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)),
+ new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2))));
+
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"#g\":[\"2vghde\",\"3abcde\"]}", encodedFilters);
+ }
+
+ @Test
+ public void testMultipleAddressableTagFilterEncoder() {
+ log.info("testMultipleAddressableTagFilterEncoder");
+
+ Integer kind = 1;
+ String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";
+ String uuidValue1 = "UUID-1";
+ String uuidValue2 = "UUID-2";
+
+ String addressableTag1 = String.join(":", String.valueOf(kind), author, uuidValue1);
+ String addressableTag2 = String.join(":", String.valueOf(kind), author, uuidValue2);
+
+ AddressTag addressTag1 = new AddressTag();
+ addressTag1.setKind(kind);
+ addressTag1.setPublicKey(new PublicKey(author));
+ addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1));
+
+ AddressTag addressTag2 = new AddressTag();
+ addressTag2.setKind(kind);
+ addressTag2.setPublicKey(new PublicKey(author));
+ addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2));
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(
+ new AddressableTagFilter<>(addressTag1),
+ new AddressableTagFilter<>(addressTag2)));
+
+ String encoded = encoder.encode();
+ String addressableTags = String.join("\",\"", addressableTag1, addressableTag2);
+ assertEquals("{\"#a\":[\"" + addressableTags + "\"]}", encoded);
+ }
+
+ @Test
+ public void testSinceFiltersEncoder() {
+ log.info("testSinceFiltersEncoder");
+
+ Long since = Date.from(Instant.now()).getTime();
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new SinceFilter(since)));
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"since\":" + since + "}", encodedFilters);
+ }
+
+ @Test
+ public void testUntilFiltersEncoder() {
+ log.info("testUntilFiltersEncoder");
+
+ Long until = Date.from(Instant.now()).getTime();
+
+ FiltersEncoder encoder = new FiltersEncoder(new Filters(new UntilFilter(until)));
+ String encodedFilters = encoder.encode();
+ assertEquals("{\"until\":" + until + "}", encodedFilters);
+ }
+
+ @Test
+ public void testReqMessageEmptyFilters() {
+ log.info("testReqMessageEmptyFilters");
+ String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9";
+
+ assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(List.of())));
+ }
+
+ @Test
+ public void testReqMessageCustomGenericTagFilter() {
+ log.info("testReqMessageEmptyFilterKey");
+ String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9";
+
+ assertDoesNotThrow(() ->
+ new ReqMessage(subscriptionId, new Filters(
+ new GenericTagQueryFilter<>(new GenericTagQuery("some-tag", "some-value")))));
+ }
+}
diff --git a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java
index c17855037..65a7aed69 100644
--- a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java
+++ b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java
@@ -1,10 +1,12 @@
package nostr.test.json;
-import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.java.Log;
import nostr.api.NIP01;
import nostr.base.Command;
import nostr.base.ElementAttribute;
+import nostr.base.GenericTagQuery;
import nostr.base.PublicKey;
import nostr.crypto.bech32.Bech32;
import nostr.event.BaseEvent;
@@ -12,30 +14,37 @@
import nostr.event.BaseTag;
import nostr.event.Kind;
import nostr.event.Marker;
-import nostr.event.impl.Filters;
+import nostr.event.filter.AddressableTagFilter;
+import nostr.event.filter.AuthorFilter;
+import nostr.event.filter.EventFilter;
+import nostr.event.filter.Filterable;
+import nostr.event.filter.Filters;
+import nostr.event.filter.GenericTagQueryFilter;
+import nostr.event.filter.IdentifierTagFilter;
+import nostr.event.filter.KindFilter;
+import nostr.event.filter.ReferencedEventFilter;
+import nostr.event.filter.ReferencedPublicKeyFilter;
import nostr.event.impl.GenericEvent;
import nostr.event.impl.GenericTag;
import nostr.event.json.codec.BaseEventEncoder;
import nostr.event.json.codec.BaseMessageDecoder;
import nostr.event.json.codec.BaseTagDecoder;
-import nostr.event.json.codec.FiltersDecoder;
-import nostr.event.json.codec.FiltersEncoder;
import nostr.event.json.codec.GenericEventDecoder;
import nostr.event.json.codec.GenericTagDecoder;
import nostr.event.message.EventMessage;
import nostr.event.message.ReqMessage;
+import nostr.event.tag.AddressTag;
import nostr.event.tag.EventTag;
+import nostr.event.tag.IdentifierTag;
import nostr.event.tag.PriceTag;
import nostr.event.tag.PubKeyTag;
import nostr.id.Identity;
-import nostr.util.NostrUtil;
+import nostr.test.util.JsonComparator;
+import nostr.util.NostrException;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -49,570 +58,619 @@
*/
@Log
public class JsonParseTest {
+ ObjectMapper mapper = new ObjectMapper();
- @Test
- public void testBaseMessageDecoder() {
- log.info("testBaseMessageDecoder");
+ @Test
+ public void testBaseMessageDecoderEventFilter() throws JsonProcessingException {
+ log.info("testBaseMessageDecoderEventFilter");
- final String parseTarget =
- "[\"REQ\", " +
- "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " +
- "{\"kinds\": [1], " +
- "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," +
- "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]";
-
- final var message = new BaseMessageDecoder<>().decode(parseTarget);
-
- assertEquals(Command.REQ.toString(), message.getCommand());
- assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId());
- assertEquals(1, ((ReqMessage) message).getFiltersList().size());
-
- var filters = ((ReqMessage) message).getFiltersList().getFirst();
-
- assertEquals(1, filters.getKinds().size());
- assertEquals(Kind.TEXT_NOTE, filters.getKinds().getFirst());
-
- assertEquals(1, filters.getAuthors().size());
- assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", filters.getAuthors().getFirst().toBech32String());
-
- assertEquals(1, filters.getReferencedEvents().size());
- assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", (filters.getReferencedEvents().getFirst().getId()));
- }
-
- @Test
- public void testBaseReqMessageDecoder() {
- log.info("testBaseReqMessageDecoder");
-
- final var filtersList = new ArrayList();
- var publicKey = Identity.generateRandomIdentity().getPublicKey();
- filtersList.add(Filters.builder().authors(new ArrayList<>(List.of(publicKey))).kinds(new ArrayList<>(List.of(Kind.CONTACT_LIST, Kind.DELETION))).build());
- filtersList.add(Filters.builder().kinds(new ArrayList<>(List.of(Kind.SET_METADATA, Kind.TEXT_NOTE))).build());
- final var reqMessage = new ReqMessage(publicKey.toString(), filtersList);
-
- assertDoesNotThrow(() -> {
- String jsonMessage = reqMessage.encode();
-
- String jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1);
- String[] parts = jsonMsg.split(",");
- assertEquals("\"REQ\"", parts[0]);
- assertEquals("\"" + publicKey.toString() + "\"", parts[1]);
- assertFalse(parts[2].startsWith("["));
- assertFalse(parts[parts.length - 1].endsWith("]"));
-
- BaseMessage message = new BaseMessageDecoder<>().decode(jsonMessage);
-
- assertEquals(reqMessage, message);
- });
- }
-
- @Test
- public void testBaseEventMessageDecoder() {
- log.info("testBaseEventMessageDecoder");
-
- final String parseTarget
- = "[\"EVENT\","
- + "{"
- + "\"content\":\"直んないわ。まあええか\","
- + "\"created_at\":1686199583,"
- + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\","
- + "\"kind\":1,"
- + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\","
- + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\","
- + "\"tags\":[]"
- + "}]";
-
- final var message = new BaseMessageDecoder<>().decode(parseTarget);
-
- assertEquals(Command.EVENT.toString(), message.getCommand());
-
- final var event = (GenericEvent) (((EventMessage) message).getEvent());
- assertEquals(1, event.getKind().intValue());
- assertEquals(1686199583, event.getCreatedAt().longValue());
- assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId());
- }
-
- @Test
- public void testBaseEventMessageMarkerDecoder() {
- log.info("testBaseEventMessageMarkerDecoder");
-
- final String json = "["
- + "\"EVENT\","
- + "{"
- + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\","
- + "\"kind\":1,"
- + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\","
- + "\"created_at\":1687765220,"
- + "\"content\":\"手順書が間違ってたら作業者は無理だな\","
- + "\"tags\":["
- + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"],"
- + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]"
- + "],"
- + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\""
- + "}]";
-
- BaseMessage message = new BaseMessageDecoder<>().decode(json);
-
- final var event = (GenericEvent) (((EventMessage) message).getEvent());
- var tags = event.getTags();
- for (BaseTag t : tags) {
- if (t.getCode().equalsIgnoreCase("e")) {
- EventTag et = (EventTag) t;
- assertEquals(Marker.ROOT, et.getMarker());
- }
- }
- }
-
- @Test
- public void testGenericTagDecoder() {
- log.info("testGenericTagDecoder");
- final String jsonString = "[\"saturn\", \"jetpack\", false]";
-
- var tag = new GenericTagDecoder<>().decode(jsonString);
-
- assertEquals("saturn", tag.getCode());
- assertEquals(2, tag.getAttributes().size());
- assertEquals("jetpack", ((ElementAttribute) (tag.getAttributes().toArray())[0]).getValue());
- assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString()));
- }
-
- @Test
- public void testClassifiedListingTagSerializer() {
- log.info("testClassifiedListingSerializer");
- final String classifiedListingEventJson = "{"
- + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\","
- + "\"kind\":30402,"
- + "\"content\":\"content ipsum\","
- + "\"pubkey\":\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\","
- + "\"created_at\":1687765220,"
- + "\"tags\":["
- + "[\"p\",\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"],"
- + "[\"title\",\"title ipsum\"],"
- + "[\"summary\",\"summary ipsum\"],"
- + "[\"published_at\",\"1687765220\"],"
- + "[\"location\",\"location ipsum\"],"
- + "[\"price\",\"11111\",\"BTC\",\"1\"]],"
- + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\""
- + "}]";
-
- GenericEvent event = new GenericEventDecoder<>().decode(classifiedListingEventJson);
- EventMessage message = NIP01.createEventMessage(event, "1");
- assertEquals(1, message.getNip());
- String encoded = new BaseEventEncoder<>((BaseEvent) message.getEvent()).encode();
- assertEquals("{\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\",\"kind\":30402,\"content\":\"content ipsum\",\"pubkey\":\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\",\"created_at\":1687765220,\"tags\":[[\"p\",\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"],[\"title\",\"title ipsum\"],[\"summary\",\"summary ipsum\"],[\"published_at\",\"1687765220\"],[\"location\",\"location ipsum\"],[\"price\",\"11111\",\"BTC\",\"1\"]],\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"}", encoded);
-
- assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", event.getId());
- assertEquals(30402, event.getKind());
- assertEquals("content ipsum", event.getContent());
- assertEquals("ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de", event.getPubKey().toString());
- assertEquals(1687765220L, event.getCreatedAt());
- assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546", event.getSignature().toString());
-
- assertEquals(new BigDecimal("11111"), event.getTags().stream().filter(baseTag ->
- baseTag.getCode().equalsIgnoreCase("price"))
- .filter(PriceTag.class::isInstance)
- .map(PriceTag.class::cast)
- .map(PriceTag::getNumber).findFirst().orElseThrow());
-
- assertEquals("BTC", event.getTags().stream().filter(baseTag ->
- baseTag.getCode().equalsIgnoreCase("price"))
- .filter(PriceTag.class::isInstance)
- .map(PriceTag.class::cast)
- .map(PriceTag::getCurrency).findFirst().orElseThrow());
-
- assertEquals("1", event.getTags().stream().filter(baseTag ->
- baseTag.getCode().equalsIgnoreCase("price"))
- .filter(PriceTag.class::isInstance)
- .map(PriceTag.class::cast)
- .map(PriceTag::getFrequency).findFirst().orElseThrow());
-
- List genericTags = event.getTags().stream()
- .filter(GenericTag.class::isInstance)
- .map(GenericTag.class::cast).toList();
-
- assertEquals("title ipsum", genericTags.stream()
- .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue());
-
- assertEquals("summary ipsum", genericTags.stream()
- .filter(tag -> tag.getCode().equalsIgnoreCase("summary")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue());
-
- assertEquals("1687765220", genericTags.stream()
- .filter(tag -> tag.getCode().equalsIgnoreCase("published_at")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue());
-
- assertEquals("location ipsum", genericTags.stream()
- .filter(tag -> tag.getCode().equalsIgnoreCase("location")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue());
- }
-
- @Test
- public void testDeserializeTag() {
- log.info("testDeserializeTag");
-
- assertDoesNotThrow(() -> {
- String npubHex = new PublicKey(NostrUtil.hexToBytes(Bech32.fromBech32("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"))).toString();
-
- final String jsonString = "[\"p\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]";
- var tag = new BaseTagDecoder<>().decode(jsonString);
-
- assertInstanceOf(PubKeyTag.class, tag);
-
- PubKeyTag pTag = (PubKeyTag) tag;
- assertEquals("wss://nostr.java", pTag.getMainRelayUrl());
- assertEquals(npubHex, pTag.getPublicKey().toString());
- assertEquals("alice", pTag.getPetName());
- });
- }
-
- @Test
- public void testDeserializeGenericTag() {
- log.info("testDeserializeGenericTag");
- assertDoesNotThrow(() -> {
- String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString();
- final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]";
- var tag = new BaseTagDecoder<>().decode(jsonString);
-
- assertInstanceOf(GenericTag.class, tag);
-
- GenericTag gTag = (GenericTag) tag;
- assertEquals("gt", gTag.getCode());
- });
- }
-
- @Test
- public void testFiltersEncoder() {
- log.info("testFiltersEncoder");
-
- String new_geohash = "2vghde";
- List