diff --git a/README.md b/README.md index 4a69431da..552f76eb4 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,4 @@ # nostr-java -A nostr client library written in java, for generating, signing and publishing events to relays. -1. Unsupported events will not be published to the relay. -2. Unsupported tags will be discarded from the event before being published. +Introducing the nostr-java library, a solution written in java for generating, signing, and publishing nostr events to relays. -## Usage -Have a look at [NostrExamples.java](https://github.com/tcheeric/nostr-java/blob/main/nostr-examples/src/main/java/nostr/examples/NostrExamples.java) in the [nostr-examples](https://github.com/tcheeric/nostr-java/tree/main/nostr-examples) folder for more details. - -## Currently Supported NIPS: - 1. NIP-01 - 2. NIP-02 - 3. NIP-03 - 4. NIP-04 - 5. NIP-05 - 6. NIP-08 - 7. NIP-09 - 8. NIP-10 - 9. NIP-12 - 10. NIP-14 - 11. NIP-16 - 12. NIP-19 (Partial: Bare keys and ids) - 13. NIP-25 - 14. NIP-26 - 15. NIP-28 \ No newline at end of file +For detailed instructions on how to utilise the library, please refer to the [nostr-client](https://github.com/tcheeric/nostr-client/) repository. \ No newline at end of file diff --git a/nostr-base/src/main/java/nostr/base/GenericTagQuery.java b/nostr-base/src/main/java/nostr/base/GenericTagQuery.java deleted file mode 100644 index 657a60970..000000000 --- a/nostr-base/src/main/java/nostr/base/GenericTagQuery.java +++ /dev/null @@ -1,19 +0,0 @@ - -package nostr.base; - -import java.util.List; -import lombok.Builder; -import lombok.Data; - -/** - * - * @author squirrel - */ -@Data -@Builder -public class GenericTagQuery { - - private final Character tagName; - private final List value; - -} diff --git a/nostr-base/src/main/java/nostr/base/IElement.java b/nostr-base/src/main/java/nostr/base/IElement.java deleted file mode 100644 index b2114ab56..000000000 --- a/nostr-base/src/main/java/nostr/base/IElement.java +++ /dev/null @@ -1,8 +0,0 @@ -package nostr.base; - -/** - * - * @author squirrel - */ -public interface IElement { -} diff --git a/nostr-base/src/main/java/nostr/base/NostrKeyPair.java b/nostr-base/src/main/java/nostr/base/NostrKeyPair.java deleted file mode 100644 index f4ca988b9..000000000 --- a/nostr-base/src/main/java/nostr/base/NostrKeyPair.java +++ /dev/null @@ -1,27 +0,0 @@ - -package nostr.base; - -import lombok.Builder; -import lombok.Data; -import lombok.ToString; -import lombok.extern.java.Log; -import nostr.base.annotation.JsonString; - -/** - * - * @author squirrel - */ -@Data -@Log -@ToString -@Builder -@Deprecated(forRemoval = true) -public final class NostrKeyPair { - - @JsonString - private final PublicKey publicKey; - - @JsonString - @ToString.Exclude - private final PrivateKey privateKey; -} diff --git a/nostr-event/README.md b/nostr-event/README.md deleted file mode 100644 index 6eea6a092..000000000 --- a/nostr-event/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# nostr-event - -We offer support for selected events and tags out of the box: - -The events: -- Event Deletion -- Encrypted Direct Messages -- Ephemeral Events -- Internet Identifier Metadata Events -- Mentions -- Set Metadata events -- Ots Events -- Reaction Events -- Replaceable Events -- Text Note Events - -The Tags: -- Deletion Tag -- Event Tag -- Nonce Tag -- Pubkey Tag -- Subject Tag - -Additionally, you may use the `GenericTag` and `GenericEvent` classes to create your custom tags and events. - -## Creating a Custom Tag -Consider the tag syntax: -`[, , , ..., ]` - -Given: - -- The tag is specified in a NIP -- The tag has a code and zero or more attributes -- An attribute may be specified by another NIP -- The tag is related to a parent event - -Practical example: - -For illustration purpose, we will implement a tag defined in **NIP-777** and with three attributes, the last attribute is specified in **NIP-888** - -Here is the corresponding java code: - -```java - // Create the attributes... - // ...using the static builder method - final ElementAttribute attr0 = ElementAttribute.builder().value(new StringValue("value 0")).build(); - - //...by invoking the constructors - final ElementAttribute attr1 = new ElementAttribute("value 1"); - final ElementAttribute attr2 = new ElementAttribute("value 2", 888);; - - Set attributes = new HashSet<>(); - attributes.add(attr0); - attributes.add(attr1); - attributes.add(attr2); - - // Create the tag - GenericTag tag = new GenericTag(777, "code", attributes); - System.out.println(tag.toString()); //["code", "value 0", "value 1", "value 2"] - - // Create the parent event - TagList tags = new TagList(); - tags.add(tag); - TextNoteEvent event = new TextNoteEvent(publicKey, tags, "Hello Nostr!"); -``` - -## Creating a Custom Event - -See my [implementation](https://github.com/tcheeric/nostr-java/blob/main/nostr-event/src/main/java/nostr/event/impl/OtsEvent.java) of the [OTS Event](https://github.com/nostr-protocol/nips/blob/master/03.md) as a cusom event. -Also, review my simple [json implementation](https://github.com/tcheeric/nostr-java/tree/0bd9a8858705e5d39ab34706ea23a584f5dfc9b6/nostr-json). \ No newline at end of file diff --git a/nostr-event/src/main/java/nostr/event/impl/ChannelCreateEvent.java b/nostr-event/src/main/java/nostr/event/impl/ChannelCreateEvent.java deleted file mode 100644 index 19c95e58a..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/ChannelCreateEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package nostr.event.impl; - -import lombok.NonNull; -import nostr.base.PublicKey; -import nostr.base.annotation.Event; -import nostr.event.Kind; -import nostr.event.list.TagList; - -/** - * @author guilhermegps - * - */ -@Event(name = "Create Channel", nip = 28) -public class ChannelCreateEvent extends GenericEvent { - - public ChannelCreateEvent(@NonNull PublicKey pubKey, @NonNull TagList tags, String content) { - super(pubKey, Kind.CHANNEL_CREATE, tags, content); - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/ChannelMetadataEvent.java b/nostr-event/src/main/java/nostr/event/impl/ChannelMetadataEvent.java deleted file mode 100644 index 6b61d9f2c..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/ChannelMetadataEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package nostr.event.impl; - -import lombok.NonNull; -import nostr.base.PublicKey; -import nostr.base.annotation.Event; -import nostr.event.Kind; -import nostr.event.list.TagList; - -/** - * @author guilhermegps - * - */ -@Event(name = "Channel Metadata", nip = 28) -public class ChannelMetadataEvent extends GenericEvent { - - public ChannelMetadataEvent(@NonNull PublicKey pubKey, @NonNull TagList tags, String content) { - super(pubKey, Kind.CHANNEL_METADATA, tags, content); - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/ClientAuthenticationEvent.java b/nostr-event/src/main/java/nostr/event/impl/ClientAuthenticationEvent.java deleted file mode 100644 index e2fe1c567..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/ClientAuthenticationEvent.java +++ /dev/null @@ -1,58 +0,0 @@ -package nostr.event.impl; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import lombok.NonNull; -import nostr.base.ElementAttribute; -import nostr.base.ITag; -import nostr.base.PublicKey; -import nostr.base.Relay; -import nostr.base.annotation.Event; -import nostr.event.Kind; -import nostr.event.list.TagList; - -/** - * - * @author squirrel - */ -@Event(name = "Authentication of clients to relays", nip = 42) -public class ClientAuthenticationEvent extends GenericEvent { - - public ClientAuthenticationEvent(@NonNull PublicKey pubKey, @NonNull TagList tags) { - super(pubKey, Kind.CLIENT_AUTH, tags); - } - - public ClientAuthenticationEvent(@NonNull PublicKey pubKey, String challenge, @NonNull Set relays) { - - Set chAttributes = new HashSet<>(); - var attribute = ElementAttribute.builder().nip(42).name("challenge").value(challenge).build(); - chAttributes.add(attribute); - - this.setTags(new TagList()); - ITag chTag = new GenericTag(42, "challenge", chAttributes); - - this.addTag(chTag); - - relays.stream().forEach(r -> { - try { - final Set relayAttributes = new HashSet<>(); - final ElementAttribute relayAttribute = getRelayAttribute(r); - relayAttributes.add(relayAttribute); - final ITag relayTag = new GenericTag(42, "relay", relayAttributes); - this.addTag(relayTag); - } catch (InterruptedException | ExecutionException ex) { - throw new RuntimeException(ex); - } - }); - - this.setPubKey(pubKey); - this.setKind(Kind.CLIENT_AUTH.getValue()); - this.setNip(42); - } - - private static ElementAttribute getRelayAttribute(Relay relay) throws ExecutionException, InterruptedException { - return ElementAttribute.builder().nip(42).name("relay").value(relay.getUri()).build(); - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/DirectMessageEvent.java b/nostr-event/src/main/java/nostr/event/impl/DirectMessageEvent.java deleted file mode 100644 index 1905e19b9..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/DirectMessageEvent.java +++ /dev/null @@ -1,18 +0,0 @@ -package nostr.event.impl; - -import nostr.base.PublicKey; -import nostr.event.Kind; -import nostr.base.annotation.Event; -import nostr.event.list.TagList; - -/** - * - * @author squirrel - */ -@Event(name = "Encrypted Direct Message", nip = 4) -public class DirectMessageEvent extends GenericEvent { - - public DirectMessageEvent(PublicKey sender, TagList tags, String content) { - super(sender, Kind.ENCRYPTED_DIRECT_MESSAGE, tags, content); - } -} diff --git a/nostr-event/src/main/java/nostr/event/impl/EphemeralEvent.java b/nostr-event/src/main/java/nostr/event/impl/EphemeralEvent.java deleted file mode 100644 index efabec24f..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/EphemeralEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package nostr.event.impl; - -import nostr.event.Kind; -import nostr.base.PublicKey; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.java.Log; -import nostr.base.annotation.Event; -import nostr.event.list.TagList; - -/** - * - * @author squirrel - */ -@Data -@Log -@EqualsAndHashCode(callSuper = false) -@Event(name = "Ephemeral Events", nip = 16) -public class EphemeralEvent extends GenericEvent { - - public EphemeralEvent(PublicKey pubKey, TagList tags, String content) { - super(pubKey, Kind.EPHEMEREAL_EVENT, tags, content); - } - - public EphemeralEvent(PublicKey pubKey, TagList tags) { - this(pubKey, tags, "..."); - } -} diff --git a/nostr-event/src/main/java/nostr/event/impl/GenericTag.java b/nostr-event/src/main/java/nostr/event/impl/GenericTag.java deleted file mode 100644 index 0cf0794ec..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/GenericTag.java +++ /dev/null @@ -1,61 +0,0 @@ -package nostr.event.impl; - -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.java.Log; -import nostr.base.ElementAttribute; -import nostr.base.IGenericElement; -import nostr.event.BaseTag; -import nostr.event.marshaller.impl.ElementMarshaller; -import nostr.util.NostrException; - -/** - * - * @author squirrel - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Log -public class GenericTag extends BaseTag implements IGenericElement { - - private final String code; - @JsonIgnore - private final Integer nip; - @JsonIgnore - private final Set attributes; - - public GenericTag(String code) { - this(1, code, new HashSet<>()); - } - - public GenericTag(Integer nip, String code) { - this(nip, code, new HashSet<>()); - } - - public GenericTag(Integer nip, String code, Set attributes) { - this.nip = nip; - this.code = code; - this.attributes = attributes; - } - - @Override - public void addAttribute(ElementAttribute attribute) { - this.attributes.add(attribute); - } - - @Override - public String toString() { - try { - return new ElementMarshaller(this, null).marshall(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - throw new RuntimeException(ex); - } - } -} diff --git a/nostr-event/src/main/java/nostr/event/impl/HideMessageEvent.java b/nostr-event/src/main/java/nostr/event/impl/HideMessageEvent.java deleted file mode 100644 index 3c5c013f8..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/HideMessageEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package nostr.event.impl; - -import lombok.NonNull; -import nostr.base.PublicKey; -import nostr.base.annotation.Event; -import nostr.event.Kind; -import nostr.event.list.TagList; - -/** - * @author guilhermegps - * - */ -@Event(name = "Hide Message on Channel", nip = 28) -public class HideMessageEvent extends GenericEvent { - - public HideMessageEvent(@NonNull PublicKey pubKey, @NonNull TagList tags, String content) { - super(pubKey, Kind.HIDE_MESSAGE, tags, content); - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java b/nostr-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java deleted file mode 100644 index f3c57593d..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java +++ /dev/null @@ -1,84 +0,0 @@ -package nostr.event.impl; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import nostr.event.Kind; -import nostr.base.PublicKey; -import java.beans.IntrospectionException; -import java.lang.reflect.InvocationTargetException; -import java.security.NoSuchAlgorithmException; -import java.util.logging.Level; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NonNull; -import lombok.extern.java.Log; -import nostr.base.Profile; -import nostr.base.annotation.Event; -import nostr.event.list.TagList; -import nostr.event.util.Nip05Validator; -import nostr.util.NostrException; - -/** - * - * @author squirrel - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Log -@Event(name = "Internet Identifier Metadata Event", nip = 5) -public final class InternetIdentifierMetadataEvent extends GenericEvent { - - private final String name; - private final String nip05; - - public InternetIdentifierMetadataEvent(PublicKey pubKey, TagList tags, @NonNull Profile profile) { - super(pubKey, Kind.SET_METADATA, tags); - this.name = profile.getName(); - this.nip05 = profile.getNip05(); - } - - @Override - public void update() throws NostrException { - - try { - // NIP-05 validator - Nip05Validator.builder().nip05(nip05).publicKey(getPubKey()).build().validate(); - - setContent(); - - super.update(); - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException ex) { - log.log(Level.SEVERE, null, ex); - throw new NostrException(ex); - } - - } - - // TODO #30 - Use jackson - private void setContent() { - - try { - Profile profile = Profile.builder().name(name).nip05(nip05).build(); - - ObjectMapper objectMapper = new ObjectMapper(); - String jsonString = objectMapper.writeValueAsString(profile); - - // Escape the JSON string - String escapedJsonString = escapeJsonString(jsonString); - - this.setContent(escapedJsonString); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } - } - - private static String escapeJsonString(String jsonString) { - return jsonString.replace("\\", "\\\\") - .replace("\"", "\\\"") - .replace("\b", "\\b") - .replace("\f", "\\f") - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("\t", "\\t"); - } -} diff --git a/nostr-event/src/main/java/nostr/event/impl/MentionsEvent.java b/nostr-event/src/main/java/nostr/event/impl/MentionsEvent.java deleted file mode 100644 index 53bba55fb..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/MentionsEvent.java +++ /dev/null @@ -1,57 +0,0 @@ - -package nostr.event.impl; - -import java.beans.IntrospectionException; -import java.lang.reflect.InvocationTargetException; -import java.security.NoSuchAlgorithmException; -import nostr.event.tag.PubKeyTag; -import nostr.event.Kind; -import nostr.base.PublicKey; -import java.util.List; -import java.util.logging.Level; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.java.Log; -import nostr.base.annotation.Event; -import nostr.event.list.PubKeyTagList; -import nostr.event.list.TagList; -import nostr.util.NostrException; - -/** - * - * @author squirrel - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Event(name = "Handling Mentions", nip = 8) -@Log -public final class MentionsEvent extends GenericEvent { - - public final PubKeyTagList mentionees; - - public MentionsEvent(PublicKey pubKey, TagList tags, String content, PubKeyTagList mentionees) { - super(pubKey, Kind.TEXT_NOTE, tags, content); - this.mentionees = mentionees; - } - - @SuppressWarnings("unchecked") - @Override - public void update() throws NostrException { - try { - super.update(); - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - log.log(Level.SEVERE, null, ex); - throw new NostrException(ex); - } - - this.getTags().addAll(mentionees); - - int index = 0; - final List pkTagList = mentionees.getList(); - - for (PubKeyTag t : pkTagList) { - String replacement = "#[" + index++ + "]"; - setContent(this.getContent().replace(t.getPublicKey().toString(), replacement)); - } - } -} diff --git a/nostr-event/src/main/java/nostr/event/impl/MetadataEvent.java b/nostr-event/src/main/java/nostr/event/impl/MetadataEvent.java deleted file mode 100644 index 1fa313f86..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/MetadataEvent.java +++ /dev/null @@ -1,80 +0,0 @@ - -package nostr.event.impl; - -import java.beans.IntrospectionException; -import java.lang.reflect.InvocationTargetException; -import java.security.NoSuchAlgorithmException; -import java.util.logging.Level; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.java.Log; -import nostr.base.IMarshaller; -import nostr.base.Profile; -import nostr.base.PublicKey; -import nostr.base.annotation.Event; -import nostr.event.Kind; -import nostr.event.list.TagList; -import nostr.util.NostrException; - -/** - * - * @author squirrel - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Event(name = "Metadata") -@Log -public final class MetadataEvent extends GenericEvent { - - private static final String NAME_PATTERN = "\\w[\\w\\-]+\\w"; - - @JsonIgnore - private Profile profile; - - public MetadataEvent(PublicKey pubKey, TagList tagList, Profile profile) throws NostrException { - super(pubKey, Kind.SET_METADATA, tagList); - this.profile = profile; - - this.validate(); - } - - private void validate() throws NostrException { - var valid = this.profile.getName().matches(NAME_PATTERN); - if (!valid) { - throw new NostrException("Invalid profile name: " + this.profile); - } - } - - @Override - public void update() throws NostrException { - setContent(); - - try { - super.update(); - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException ex) { - log.log(Level.SEVERE, null, ex); - throw new NostrException(ex); - } - } - - private void setContent() { - var mapper = IMarshaller.MAPPER; - try { - ObjectNode objNode = JsonNodeFactory.instance.objectNode(); - objNode.set("name", mapper.valueToTree(this.getProfile().getName())); - objNode.set("about", mapper.valueToTree(this.getProfile().getAbout())); - objNode.set("picture", mapper.valueToTree(this.getProfile().getPicture().toString())); - - setContent(mapper.writeValueAsString(objNode)); - } catch (Exception e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/MuteUserEvent.java b/nostr-event/src/main/java/nostr/event/impl/MuteUserEvent.java deleted file mode 100644 index 2f866db33..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/MuteUserEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package nostr.event.impl; - -import lombok.NonNull; -import nostr.base.PublicKey; -import nostr.base.annotation.Event; -import nostr.event.Kind; -import nostr.event.list.TagList; - -/** - * @author guilhermegps - * - */ -@Event(name = "Mute User on Channel", nip = 28) -public class MuteUserEvent extends GenericEvent { - - public MuteUserEvent(@NonNull PublicKey pubKey, @NonNull TagList tags, String content) { - super(pubKey, Kind.MUTE_USER, tags, content); - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/ReactionEvent.java b/nostr-event/src/main/java/nostr/event/impl/ReactionEvent.java deleted file mode 100644 index 5de168fa2..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/ReactionEvent.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.event.impl; - -import nostr.event.Kind; -import nostr.base.PublicKey; -import nostr.event.Reaction; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.java.Log; -import nostr.base.annotation.Event; -import nostr.event.list.TagList; - -/** - * - * @author squirrel - */ -@Data -@Log -@EqualsAndHashCode(callSuper = false) -@Event(name = "Reactions", nip = 25) -public class ReactionEvent extends GenericEvent { - - private final GenericEvent sourceEvent; - - public ReactionEvent(PublicKey pubKey, TagList tags, Reaction content, GenericEvent sourceEvent) { - super(pubKey, Kind.REACTION, tags, content.getEmoji()); - this.sourceEvent = sourceEvent; - } - -} diff --git a/nostr-event/src/main/java/nostr/event/impl/ReplaceableEvent.java b/nostr-event/src/main/java/nostr/event/impl/ReplaceableEvent.java deleted file mode 100644 index 780a27700..000000000 --- a/nostr-event/src/main/java/nostr/event/impl/ReplaceableEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package nostr.event.impl; - -import nostr.event.Kind; -import nostr.base.PublicKey; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.java.Log; -import nostr.base.annotation.Event; -import nostr.event.list.TagList; - -/** - * - * @author squirrel - */ -@Data -@Log -@EqualsAndHashCode(callSuper = false) -@Event(name = "Replaceable Events", nip = 16) -public class ReplaceableEvent extends GenericEvent { - - private final GenericEvent original; - - public ReplaceableEvent(PublicKey pubKey, TagList tags, String content, GenericEvent original) { - super(pubKey, Kind.DELETION, tags, content); - this.original = original; - } - -} diff --git a/nostr-event/src/main/java/nostr/event/list/PubKeyTagList.java b/nostr-event/src/main/java/nostr/event/list/PubKeyTagList.java deleted file mode 100644 index ccc945317..000000000 --- a/nostr-event/src/main/java/nostr/event/list/PubKeyTagList.java +++ /dev/null @@ -1,24 +0,0 @@ -package nostr.event.list; - -import nostr.event.tag.PubKeyTag; -import java.util.ArrayList; -import java.util.List; -import lombok.extern.java.Log; -import nostr.base.annotation.JsonList; - -/** - * - * @author squirrel - */ -@Log -@JsonList -public class PubKeyTagList extends TagList { - - public PubKeyTagList() { - this(new ArrayList<>()); - } - - private PubKeyTagList(List list) { - super(list); - } -} diff --git a/nostr-event/src/main/java/nostr/event/list/TagList.java b/nostr-event/src/main/java/nostr/event/list/TagList.java deleted file mode 100644 index 11ffa0e5a..000000000 --- a/nostr-event/src/main/java/nostr/event/list/TagList.java +++ /dev/null @@ -1,29 +0,0 @@ - -package nostr.event.list; - -import java.util.ArrayList; -import java.util.List; - -import lombok.NonNull; -import nostr.base.ITag; -import nostr.base.annotation.JsonList; - -/** - * - * @author squirrel - */ -@SuppressWarnings("rawtypes") -@JsonList -public class TagList extends BaseList { - - @SuppressWarnings("unchecked") - public TagList() { - this(new ArrayList<>()); - } - - @SuppressWarnings("unchecked") - protected TagList(@NonNull List list) { - super(list); - } - -} diff --git a/nostr-event/src/main/java/nostr/event/marshaller/impl/GenericTagMarshaller.java b/nostr-event/src/main/java/nostr/event/marshaller/impl/GenericTagMarshaller.java deleted file mode 100644 index 551747ec1..000000000 --- a/nostr-event/src/main/java/nostr/event/marshaller/impl/GenericTagMarshaller.java +++ /dev/null @@ -1,48 +0,0 @@ -package nostr.event.marshaller.impl; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import nostr.base.GenericTagQuery; -import nostr.base.IMarshaller; -import nostr.base.Relay; -import nostr.event.serializer.CustomGenericTagSerializer; -import nostr.util.NostrException; - -/** - * @author guilhermegps - * - */ -@AllArgsConstructor -@Data -@Builder -public class GenericTagMarshaller implements IMarshaller { - - private final GenericTagQuery tag; - private final Relay relay; - - @Override - public String marshall() throws NostrException { - return toJson(); - } - - @Override - public String toJson() throws NostrException { - try { - SimpleModule module = new SimpleModule(); - module.addSerializer(new CustomGenericTagSerializer()); - var mappe = (new ObjectMapper()) - .setSerializationInclusion(Include.NON_NULL) - .registerModule(module); - - return mappe.writeValueAsString(tag); - } catch (Exception e) { - throw new NostrException(e); - } - } - -} diff --git a/nostr-event/src/main/java/nostr/event/marshaller/impl/TagMarshaller.java b/nostr-event/src/main/java/nostr/event/marshaller/impl/TagMarshaller.java deleted file mode 100644 index 958d11ebf..000000000 --- a/nostr-event/src/main/java/nostr/event/marshaller/impl/TagMarshaller.java +++ /dev/null @@ -1,48 +0,0 @@ -package nostr.event.marshaller.impl; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import nostr.base.IMarshaller; -import nostr.base.ITag; -import nostr.base.Relay; -import nostr.event.serializer.CustomTagSerializer; -import nostr.util.NostrException; - -/** - * @author guilhermegps - * - */ -@AllArgsConstructor -@Data -@Builder -public class TagMarshaller implements IMarshaller { - - private final ITag tag; - private final Relay relay; - - @Override - public String marshall() throws NostrException { - return toJson(); - } - - @Override - public String toJson() throws NostrException { - try { - SimpleModule module = new SimpleModule(); - module.addSerializer(new CustomTagSerializer()); - var mappe = (new ObjectMapper()) - .setSerializationInclusion(Include.NON_NULL) - .registerModule(module); - - return mappe.writeValueAsString(tag); - } catch (Exception e) { - throw new NostrException(e); - } - } - -} diff --git a/nostr-event/src/main/java/nostr/event/serializer/CustomBaseListSerializer.java b/nostr-event/src/main/java/nostr/event/serializer/CustomBaseListSerializer.java deleted file mode 100644 index fb5b2ff5c..000000000 --- a/nostr-event/src/main/java/nostr/event/serializer/CustomBaseListSerializer.java +++ /dev/null @@ -1,65 +0,0 @@ -package nostr.event.serializer; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - -import lombok.extern.java.Log; -import nostr.base.IMarshaller; -import nostr.event.list.BaseList; - - -/** - * @author guilhermegps - * - */ -@Log -public class CustomBaseListSerializer extends JsonSerializer { - - @Override - public void serialize(BaseList value, JsonGenerator gen, SerializerProvider serializers) { - try { - var list = value.getList().parallelStream().map(obj -> toJson(obj)) - .collect(Collectors.toList()); - - gen.writePOJO(list); - } catch (IOException e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - - protected JsonNode toJson(Object obj) { - var mapper = IMarshaller.MAPPER; - try { - JsonNode node = mapper.valueToTree(obj); - - if(node.isObject()) { - Iterator> fields = node.fields(); - - var list = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(fields, Spliterator.ORDERED), false) - .map(f -> f.getValue().asText().toLowerCase() ) - .collect(Collectors.toList()); - - return mapper.valueToTree(list); - } - - return node; - } catch (Exception e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - -} diff --git a/nostr-event/src/main/java/nostr/event/serializer/CustomGenericTagListSerializer.java b/nostr-event/src/main/java/nostr/event/serializer/CustomGenericTagListSerializer.java deleted file mode 100644 index fcbfd7f7b..000000000 --- a/nostr-event/src/main/java/nostr/event/serializer/CustomGenericTagListSerializer.java +++ /dev/null @@ -1,55 +0,0 @@ -package nostr.event.serializer; - -import java.io.IOException; -import java.util.logging.Level; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import lombok.extern.java.Log; -import nostr.base.GenericTagQuery; -import nostr.base.IMarshaller; -import nostr.event.list.GenericTagQueryList; - - -/** - * @author guilhermegps - * - */ -@Log -public class CustomGenericTagListSerializer extends JsonSerializer { - - @Override - public void serialize(GenericTagQueryList value, JsonGenerator gen, SerializerProvider serializers) { - try { - var list = value.getList().parallelStream().map(gtq -> toJson(gtq)) - .collect(Collectors.toList()); - - gen.writePOJO(list); - } catch (IOException e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - - protected JsonNode toJson(GenericTagQuery gtq) { - var mapper = IMarshaller.MAPPER; - try { - JsonNode node = mapper.valueToTree(gtq); - ObjectNode objNode = (ObjectNode) node; - objNode.set("#" + node.get("tagName").textValue(), node.get("value")); - objNode.remove("tagName"); - objNode.remove("value"); - - return node; - } catch (Exception e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - -} diff --git a/nostr-event/src/main/java/nostr/event/serializer/CustomGenericTagSerializer.java b/nostr-event/src/main/java/nostr/event/serializer/CustomGenericTagSerializer.java deleted file mode 100644 index 0d3d384c3..000000000 --- a/nostr-event/src/main/java/nostr/event/serializer/CustomGenericTagSerializer.java +++ /dev/null @@ -1,56 +0,0 @@ -package nostr.event.serializer; - -import java.io.IOException; -import java.util.logging.Level; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import lombok.extern.java.Log; -import nostr.base.GenericTagQuery; -import nostr.base.IMarshaller; - - -/** - * @author guilhermegps - * - */ -@Log -public class CustomGenericTagSerializer extends StdSerializer { - - private static final long serialVersionUID = 6803478463890319884L; - - public CustomGenericTagSerializer() { - super(GenericTagQuery.class); - } - - @Override - public void serialize(GenericTagQuery value, JsonGenerator gen, SerializerProvider serializers) { - try { - gen.writePOJO(toJson(value)); - } catch (IOException e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - - public static JsonNode toJson(GenericTagQuery gtq) { - var mapper = IMarshaller.MAPPER; - try { - JsonNode node = mapper.valueToTree(gtq); - ObjectNode objNode = (ObjectNode) node; - objNode.set("#" + node.get("tagName").textValue(), node.get("value")); - objNode.remove("tagName"); - objNode.remove("value"); - - return node; - } catch (Exception e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - -} diff --git a/nostr-event/src/main/java/nostr/event/serializer/CustomTagSerializer.java b/nostr-event/src/main/java/nostr/event/serializer/CustomTagSerializer.java deleted file mode 100644 index 7d1981580..000000000 --- a/nostr-event/src/main/java/nostr/event/serializer/CustomTagSerializer.java +++ /dev/null @@ -1,53 +0,0 @@ -package nostr.event.serializer; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import lombok.extern.java.Log; -import nostr.base.IMarshaller; -import nostr.base.ITag; - -/** - * @author guilhermegps - * - */ -@Log -public class CustomTagSerializer extends StdSerializer { - - private static final long serialVersionUID = -3877972991082754068L; - - public CustomTagSerializer() { - super(ITag.class); - } - - @Override - public void serialize(ITag value, JsonGenerator gen, SerializerProvider serializers) { - try { - var mapper = IMarshaller.MAPPER; - JsonNode node = mapper.valueToTree(value); - - Iterator> fields = node.fields(); - var list = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(fields, Spliterator.ORDERED), false) - .map(f -> f.getValue().asText().toLowerCase() ) - .collect(Collectors.toList()); - - gen.writePOJO(list); - } catch (IOException e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - -} \ No newline at end of file diff --git a/nostr-examples/src/main/java/nostr/examples/NostrExamples.java b/nostr-examples/src/main/java/nostr/examples/NostrExamples.java deleted file mode 100644 index bff3dd9d0..000000000 --- a/nostr-examples/src/main/java/nostr/examples/NostrExamples.java +++ /dev/null @@ -1,657 +0,0 @@ -package nostr.examples; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.LogManager; - -import lombok.extern.java.Log; -import nostr.base.Channel; -import nostr.base.ContentReason; -import nostr.base.ITag; -import nostr.base.Profile; -import nostr.base.PublicKey; -import nostr.event.Kind; -import nostr.event.Marker; -import nostr.event.Reaction; -import nostr.event.impl.ChannelCreateEvent; -import nostr.event.impl.ChannelMessageEvent; -import nostr.event.impl.ChannelMetadataEvent; -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.GenericMessage; -import nostr.event.impl.HideMessageEvent; -import nostr.event.impl.InternetIdentifierMetadataEvent; -import nostr.event.impl.MentionsEvent; -import nostr.event.impl.MetadataEvent; -import nostr.event.impl.MuteUserEvent; -import nostr.event.impl.ReactionEvent; -import nostr.event.impl.ReplaceableEvent; -import nostr.event.impl.TextNoteEvent; -import nostr.event.list.KindList; -import nostr.event.list.PubKeyTagList; -import nostr.event.list.TagList; -import nostr.event.message.EventMessage; -import nostr.event.message.ReqMessage; -import nostr.event.tag.EventTag; -import nostr.event.tag.PubKeyTag; -import nostr.id.Client; -import nostr.id.Identity; -import nostr.util.NostrException; -import nostr.util.UnsupportedNIPException; - -/** - * - * @author squirrel - */ -@Log -public class NostrExamples { - - private static final Identity RECEIVER = Identity.generateRandomIdentity(); - private static final Identity SENDER = Identity.generateRandomIdentity(); - private static final Profile PROFILE = Profile.builder() - .name("test") - .about("Hey, it's me!") - .publicKey(SENDER.getPublicKey()) - .build(); - private final static Map RELAYS = Map.of("brb", "brb.io", "damus", "relay.damus.io", "ZBD", "nostr.zebedee.cloud", "taxi", "relay.taxi", "vision", "relay.nostr.vision"); - //private final static Client CLIENT = new Client(RELAYS); - private final static Client CLIENT = Client.getInstance(RELAYS); - - static { - final LogManager logManager = LogManager.getLogManager(); - try (final InputStream is = NostrExamples.class.getResourceAsStream("/logging.properties")) { - logManager.readConfiguration(is); - } catch (IOException ex) { - System.exit(-1000); - } - - try { - PROFILE.setPicture(new URL("https://images.unsplash.com/photo-1462888210965-cdf193fb74de")); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) throws IOException, Exception { - try { -// Wait it until tried to connect to a half of relays - while(CLIENT.getThreadPool().getCompletedTaskCount() < (RELAYS.size()/2)) { - Thread.sleep(5000); - } - - log.log(Level.FINE, "================= The Beginning"); - logAccountsData(); - - ExecutorService executor = Executors.newFixedThreadPool(10); - - executor.submit(() -> { - try { - sendTextNoteEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - sendEncryptedDirectMessage(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - mentionsEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - deletionEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - metaDataEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - ephemerealEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - reactionEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - replaceableEvent(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - internetIdMetadata(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - filters(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - createChannel(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - updateChannelMetadata(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - sendChannelMessage(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - hideMessage(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - executor.submit(() -> { - try { - muteUser(); - } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); - } - }); - - stop(executor); - - if (executor.isTerminated()) { - log.log(Level.FINE, "================== The End"); - } - - } catch (IllegalArgumentException ex) { - log.log(Level.SEVERE, null, ex); - throw new NostrException(ex); - } - } - - private static void sendTextNoteEvent() throws NostrException { - logHeader("sendTextNoteEvent"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkSenderTag); - - GenericEvent event = new TextNoteEvent(publicKeySender, tagList, - "Hello world, I'm here on nostr-java API!"); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void sendEncryptedDirectMessage() throws NostrException { - logHeader("sendEncryptedDirectMessage"); - - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkeyRcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).build(); - TagList tagList = new TagList(); - tagList.add(pkeyRcptTag); - - var event2 = new DirectMessageEvent(publicKeySender, tagList, "Hello Nakamoto!"); - - SENDER.encryptDirectMessage(event2); - SENDER.sign(event2); - - GenericMessage message = new EventMessage(event2); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void mentionsEvent() throws NostrException { - logHeader("mentionsEvent"); - - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkeySenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkeySenderTag); - - PubKeyTagList mentionees = new PubKeyTagList(); - mentionees.add(PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).build()); - - GenericEvent event = new MentionsEvent(publicKeySender, tagList, "Hello " + RECEIVER.getPublicKey().toString(), mentionees); - SENDER.sign(event); - - log.log(Level.FINER, ">>>>>>>>>>>> Event: {0}", event); - - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void deletionEvent() throws NostrException { - logHeader("deletionEvent"); - - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkSenderTag); - - GenericEvent event = new TextNoteEvent(publicKeySender, tagList, "Hello Astral, Please delete me!"); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - tagList = new TagList(); - tagList.add(EventTag.builder().idEvent(event.getId()).build()); - GenericEvent delEvent = new DeletionEvent(publicKeySender, tagList); - - SENDER.sign(delEvent); - message = new EventMessage(delEvent); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void metaDataEvent() throws NostrException { - logHeader("metaDataEvent"); - - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - TagList tagList = new TagList(); - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - tagList.add(pkSenderTag); - - var event = new MetadataEvent(publicKeySender, tagList, PROFILE); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void ephemerealEvent() throws NostrException { - logHeader("ephemerealEvent"); - - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkSenderTag); - - GenericEvent event = new EphemeralEvent(publicKeySender, tagList); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void reactionEvent() throws NostrException { - logHeader("reactionEvent"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkSenderTag); - - GenericEvent event = new TextNoteEvent(publicKeySender, tagList, "Hello Astral, Please like me!"); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - tagList = new TagList(); - tagList.add(EventTag.builder().idEvent(event.getId()).build()); - tagList.add(PubKeyTag.builder().publicKey(publicKeySender).build()); - GenericEvent reactionEvent = new ReactionEvent(publicKeySender, tagList, Reaction.LIKE, event); - - SENDER.sign(reactionEvent); - message = new EventMessage(reactionEvent); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void replaceableEvent() throws NostrException { - logHeader("replaceableEvent"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkSenderTag); - - GenericEvent event = new TextNoteEvent(publicKeySender, tagList, "Hello Astral, Please replace me!"); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - tagList = new TagList(); - tagList.add(EventTag.builder().idEvent(event.getId()).build()); - GenericEvent replaceableEvent = new ReplaceableEvent(publicKeySender, tagList, "New content", event); - - SENDER.sign(replaceableEvent); - message = new EventMessage(replaceableEvent); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - private static void internetIdMetadata() throws NostrException { - logHeader("internetIdMetadata"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - ITag pkSenderTag = PubKeyTag.builder().publicKey(publicKeySender).petName("nostr-java").build(); - TagList tagList = new TagList(); - tagList.add(pkSenderTag); - - GenericEvent event = new InternetIdentifierMetadataEvent(publicKeySender, tagList, PROFILE); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - } catch (UnsupportedNIPException ex) { - log.log(Level.WARNING, null, ex); - } - } - - // FIXME - public static void filters() throws NostrException { - logHeader("filters"); - try { - KindList kindList = new KindList(); - kindList.add(Kind.EPHEMEREAL_EVENT); - kindList.add(Kind.TEXT_NOTE); - - Filters filters = Filters.builder().kinds(kindList).limit(10).build(); - - String subId = "subId" + System.currentTimeMillis(); - GenericMessage message = new ReqMessage(subId, filters); - - CLIENT.send(message); - } catch (Exception ex) { - throw new NostrException(ex); - } - } - - private static GenericEvent createChannel() throws NostrException { - logHeader("createChannel"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - var channel = Channel.builder().name("JNostr Channel") - .about("This is a channel to test NIP28 in nostr-java") - .picture("https://cdn.pixabay.com/photo/2020/05/19/13/48/cartoon-5190942_960_720.jpg").build(); - GenericEvent event = new ChannelCreateEvent(publicKeySender, new TagList(), channel.toString()); - - SENDER.sign(event); - GenericMessage message = new EventMessage(event); - - CLIENT.send(message); - - return event; - } catch (Exception ex) { - throw new NostrException(ex); - } - } - - private static void updateChannelMetadata() throws NostrException { - logHeader("updateChannelMetadata"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - var channelCreateEvent = createChannel(); - - var tagList = new TagList(); - tagList.add(EventTag.builder().idEvent(channelCreateEvent.getId()) - .recommendedRelayUrl(CLIENT.getRelays().stream().findFirst().get().getUri()).build()); - - var channel = Channel.builder().name("test change name") - .about("This is a channel to test NIP28 in nostr-java | changed") - .picture("https://cdn.pixabay.com/photo/2020/05/19/13/48/cartoon-5190942_960_720.jpg").build(); - GenericEvent event = new ChannelMetadataEvent(publicKeySender, tagList, channel.toString()); - - SENDER.sign(event); - var message = new EventMessage(event); - - CLIENT.send(message); - } catch (Exception ex) { - throw new NostrException(ex); - } - } - - private static GenericEvent sendChannelMessage() throws NostrException { - logHeader("sendChannelMessage"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - var channelCreateEvent = createChannel(); - - var tagList = new TagList(); - tagList.add(EventTag.builder().idEvent(channelCreateEvent.getId()) - .recommendedRelayUrl(CLIENT.getRelays().stream().findFirst().get().getUri()) - .marker(Marker.ROOT) - .build()); - - GenericEvent event = new ChannelMessageEvent(publicKeySender, tagList, "Hello everybody!"); - - SENDER.sign(event); - var message = new EventMessage(event); - - CLIENT.send(message); - - return event; - } catch (Exception ex) { - throw new NostrException(ex); - } - } - - private static GenericEvent hideMessage() throws NostrException { - logHeader("hideMessage"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - var channelMessageEvent = sendChannelMessage(); - - var tagList = new TagList(); - tagList.add(EventTag.builder().idEvent(channelMessageEvent.getId()).build()); - - GenericEvent event = new HideMessageEvent(publicKeySender, tagList, - ContentReason.builder().reason("Dick pic").build().toString()); - - SENDER.sign(event); - var message = new EventMessage(event); - - CLIENT.send(message); - - return event; - } catch (Exception ex) { - throw new NostrException(ex); - } - } - - private static GenericEvent muteUser() throws NostrException { - logHeader("muteUser"); - try { - final PublicKey publicKeySender = SENDER.getPublicKey(); - - var tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).build()); - - GenericEvent event = new MuteUserEvent(publicKeySender, tagList, - ContentReason.builder().reason("Posting dick pics").build().toString()); - - SENDER.sign(event); - var message = new EventMessage(event); - - CLIENT.send(message); - - return event; - } catch (Exception ex) { - throw new NostrException(ex); - } - } - -// public static void sensitiveContentNote(Identity wallet, Client client) throws NostrException { -// logHeader("sensitiveContentNote"); -// try { -// // Create the attribute value list -// List values = new ArrayList<>(); -// values.add("sensitive content"); -// -// // Create the attributes -// final ElementAttribute attr = ElementAttribute.builder().nip(36).isString(true).name("reason").valueList(values).build(); -// Set attributes = new HashSet<>(); -// attributes.add(attr); -// -// GenericTag sensitiveContentTag = new GenericTag(1, "", attributes); -// } catch (UnsupportedNIPException ex) { -// log.log(Level.WARNING, null, ex); -// } catch (Exception ex) { -// throw new NostrException(ex); -// } -// -// } - - private static void logAccountsData() throws NostrException { - StringBuilder msg = new StringBuilder("################################ ACCOUNTS BEGINNING ################################") - .append('\n').append("*** RECEIVER ***").append('\n') - .append('\n').append("* PrivateKey: ").append(RECEIVER.getPrivateKey().getBech32()) - .append('\n').append("* PrivateKey HEX: ").append(RECEIVER.getPrivateKey().toString()) - .append('\n').append("* PublicKey: ").append(RECEIVER.getPublicKey().getBech32()) - .append('\n').append("* PublicKey HEX: ").append(RECEIVER.getPublicKey().toString()) - .append('\n').append('\n').append("*** SENDER ***").append('\n') - .append('\n').append("* PrivateKey: ").append(SENDER.getPrivateKey().getBech32()) - .append('\n').append("* PrivateKey HEX: ").append(SENDER.getPrivateKey().toString()) - .append('\n').append("* PublicKey: ").append(SENDER.getPublicKey().getBech32()) - .append('\n').append("* PublicKey HEX: ").append(SENDER.getPublicKey().toString()) - .append('\n').append('\n').append("################################ ACCOUNTS END ################################"); - - log.log(Level.INFO, msg.toString()); - } - - private static void logHeader(String header) { - for (int i = 0; i < 30; i++) { - System.out.print("#"); - } - System.out.println(); - System.out.println("\t" + header); - for (int i = 0; i < 30; i++) { - System.out.print("#"); - } - System.out.println(); - } - - private static void stop(ExecutorService executor) { - try { - executor.shutdown(); - executor.awaitTermination(60, TimeUnit.SECONDS); - } catch (InterruptedException e) { - log.log(Level.SEVERE, "termination interrupted"); - } finally { - if (!executor.isTerminated()) { - log.log(Level.SEVERE, "killing non-finished tasks"); - } - executor.shutdownNow(); - } - } -} diff --git a/nostr-base/nb-configuration.xml b/nostr-java-base/nb-configuration.xml similarity index 100% rename from nostr-base/nb-configuration.xml rename to nostr-java-base/nb-configuration.xml diff --git a/nostr-base/pom.xml b/nostr-java-base/pom.xml similarity index 86% rename from nostr-base/pom.xml rename to nostr-java-base/pom.xml index 8b3245ca8..02f7f8411 100644 --- a/nostr-base/pom.xml +++ b/nostr-java-base/pom.xml @@ -6,10 +6,10 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-base + nostr-java-base jar @@ -26,12 +26,12 @@ ${project.groupId} - nostr-util + nostr-java-util ${project.version} ${project.groupId} - nostr-crypto + nostr-java-crypto ${project.version} diff --git a/nostr-base/src/main/java/module-info.java b/nostr-java-base/src/main/java/module-info.java similarity index 100% rename from nostr-base/src/main/java/module-info.java rename to nostr-java-base/src/main/java/module-info.java diff --git a/nostr-base/src/main/java/nostr/base/BaseKey.java b/nostr-java-base/src/main/java/nostr/base/BaseKey.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/BaseKey.java rename to nostr-java-base/src/main/java/nostr/base/BaseKey.java diff --git a/nostr-base/src/main/java/nostr/base/Channel.java b/nostr-java-base/src/main/java/nostr/base/Channel.java similarity index 66% rename from nostr-base/src/main/java/nostr/base/Channel.java rename to nostr-java-base/src/main/java/nostr/base/Channel.java index 80fabfa61..3e678ce52 100644 --- a/nostr-base/src/main/java/nostr/base/Channel.java +++ b/nostr-java-base/src/main/java/nostr/base/Channel.java @@ -21,16 +21,16 @@ public class Channel { private String about; private String picture; - + @Override public String toString() { - ObjectMapper mapper = new ObjectMapper(); - - try { - return mapper.writeValueAsString(this); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } + ObjectMapper mapper = new ObjectMapper(); + + try { + return mapper.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } } diff --git a/nostr-java-base/src/main/java/nostr/base/ChannelProfile.java b/nostr-java-base/src/main/java/nostr/base/ChannelProfile.java new file mode 100644 index 000000000..f95d32260 --- /dev/null +++ b/nostr-java-base/src/main/java/nostr/base/ChannelProfile.java @@ -0,0 +1,20 @@ +package nostr.base; + +import java.net.URL; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * + * @author eric + */ +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class ChannelProfile extends Profile { + + public ChannelProfile(String name, String about, URL picture) { + super(name, about, picture); + } +} diff --git a/nostr-base/src/main/java/nostr/base/Command.java b/nostr-java-base/src/main/java/nostr/base/Command.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/Command.java rename to nostr-java-base/src/main/java/nostr/base/Command.java diff --git a/nostr-base/src/main/java/nostr/base/ContentReason.java b/nostr-java-base/src/main/java/nostr/base/ContentReason.java similarity index 64% rename from nostr-base/src/main/java/nostr/base/ContentReason.java rename to nostr-java-base/src/main/java/nostr/base/ContentReason.java index 35c11ed9d..318960a8d 100644 --- a/nostr-base/src/main/java/nostr/base/ContentReason.java +++ b/nostr-java-base/src/main/java/nostr/base/ContentReason.java @@ -17,16 +17,16 @@ public class ContentReason { private String reason; - + @Override public String toString() { - ObjectMapper mapper = new ObjectMapper(); - - try { - return mapper.writeValueAsString(this); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } + ObjectMapper mapper = new ObjectMapper(); + + try { + return mapper.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } } diff --git a/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java b/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java new file mode 100644 index 000000000..e15e92013 --- /dev/null +++ b/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java @@ -0,0 +1,36 @@ +package nostr.base; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * + * @author squirrel + */ +@Builder +@Data +@ToString +@EqualsAndHashCode +@AllArgsConstructor +public class ElementAttribute { + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + @EqualsAndHashCode.Exclude + private final String name; + + @JsonProperty + @EqualsAndHashCode.Include + private final Object value; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + @EqualsAndHashCode.Exclude + private final Integer nip; + +} diff --git a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java new file mode 100644 index 000000000..6ab3921eb --- /dev/null +++ b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java @@ -0,0 +1,23 @@ + +package nostr.base; + +import java.util.List; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * + * @author squirrel + */ +@Data +@NoArgsConstructor +public class GenericTagQuery implements IElement { + + private Character tagName; + private List value; + + @Override + public Integer getNip() { + return 1; + } +} diff --git a/nostr-base/src/main/java/nostr/base/IBech32Encodable.java b/nostr-java-base/src/main/java/nostr/base/IBech32Encodable.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/IBech32Encodable.java rename to nostr-java-base/src/main/java/nostr/base/IBech32Encodable.java diff --git a/nostr-java-base/src/main/java/nostr/base/IDecoder.java b/nostr-java-base/src/main/java/nostr/base/IDecoder.java new file mode 100644 index 000000000..4b0871192 --- /dev/null +++ b/nostr-java-base/src/main/java/nostr/base/IDecoder.java @@ -0,0 +1,14 @@ +package nostr.base; + +import nostr.util.NostrException; + +/** + * + * @author eric + * @param + */ +public interface IDecoder { + + public abstract T decode() throws NostrException; + +} diff --git a/nostr-java-base/src/main/java/nostr/base/IElement.java b/nostr-java-base/src/main/java/nostr/base/IElement.java new file mode 100644 index 000000000..cf204ddad --- /dev/null +++ b/nostr-java-base/src/main/java/nostr/base/IElement.java @@ -0,0 +1,11 @@ +package nostr.base; + +/** + * + * @author squirrel + */ +public interface IElement { + + public Integer getNip(); + +} diff --git a/nostr-base/src/main/java/nostr/base/IMarshaller.java b/nostr-java-base/src/main/java/nostr/base/IEncoder.java similarity index 68% rename from nostr-base/src/main/java/nostr/base/IMarshaller.java rename to nostr-java-base/src/main/java/nostr/base/IEncoder.java index e76fb83e0..ffe5139f3 100644 --- a/nostr-base/src/main/java/nostr/base/IMarshaller.java +++ b/nostr-java-base/src/main/java/nostr/base/IEncoder.java @@ -9,11 +9,10 @@ /** * * @author squirrel + * @param */ -public interface IMarshaller { +public interface IEncoder { public static final ObjectMapper MAPPER = new ObjectMapper().setSerializationInclusion(Include.NON_NULL); - public abstract String marshall() throws NostrException; - - public String toJson() throws NostrException; + public abstract String encode() throws NostrException; } diff --git a/nostr-base/src/main/java/nostr/base/IEvent.java b/nostr-java-base/src/main/java/nostr/base/IEvent.java similarity index 74% rename from nostr-base/src/main/java/nostr/base/IEvent.java rename to nostr-java-base/src/main/java/nostr/base/IEvent.java index fc99c3ed1..76d8984fb 100644 --- a/nostr-base/src/main/java/nostr/base/IEvent.java +++ b/nostr-java-base/src/main/java/nostr/base/IEvent.java @@ -5,6 +5,4 @@ * @author squirrel */ public interface IEvent extends IElement, IBech32Encodable { - - public Integer getNip(); } diff --git a/nostr-base/src/main/java/nostr/base/IGenericElement.java b/nostr-java-base/src/main/java/nostr/base/IGenericElement.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/IGenericElement.java rename to nostr-java-base/src/main/java/nostr/base/IGenericElement.java diff --git a/nostr-base/src/main/java/nostr/base/IHandler.java b/nostr-java-base/src/main/java/nostr/base/IHandler.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/IHandler.java rename to nostr-java-base/src/main/java/nostr/base/IHandler.java diff --git a/nostr-base/src/main/java/nostr/base/IKey.java b/nostr-java-base/src/main/java/nostr/base/IKey.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/IKey.java rename to nostr-java-base/src/main/java/nostr/base/IKey.java diff --git a/nostr-base/src/main/java/nostr/base/INostrList.java b/nostr-java-base/src/main/java/nostr/base/INostrList.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/INostrList.java rename to nostr-java-base/src/main/java/nostr/base/INostrList.java diff --git a/nostr-base/src/main/java/nostr/base/ISignable.java b/nostr-java-base/src/main/java/nostr/base/ISignable.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/ISignable.java rename to nostr-java-base/src/main/java/nostr/base/ISignable.java diff --git a/nostr-base/src/main/java/nostr/base/ITag.java b/nostr-java-base/src/main/java/nostr/base/ITag.java similarity index 57% rename from nostr-base/src/main/java/nostr/base/ITag.java rename to nostr-java-base/src/main/java/nostr/base/ITag.java index 42861e721..eb7a4f537 100644 --- a/nostr-base/src/main/java/nostr/base/ITag.java +++ b/nostr-java-base/src/main/java/nostr/base/ITag.java @@ -1,8 +1,6 @@ package nostr.base; -import nostr.util.NostrException; - /** * * @author squirrel @@ -13,5 +11,5 @@ public interface ITag extends IElement { public abstract String getCode(); - public abstract String printAttributes(Relay relay, boolean escape) throws NostrException; +// public abstract String printAttributes(Relay relay, boolean escape) throws NostrException; } diff --git a/nostr-base/src/main/java/nostr/base/IUnmarshaller.java b/nostr-java-base/src/main/java/nostr/base/IUnmarshaller.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/IUnmarshaller.java rename to nostr-java-base/src/main/java/nostr/base/IUnmarshaller.java diff --git a/nostr-base/src/main/java/nostr/base/KeyType.java b/nostr-java-base/src/main/java/nostr/base/KeyType.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/KeyType.java rename to nostr-java-base/src/main/java/nostr/base/KeyType.java diff --git a/nostr-base/src/main/java/nostr/base/NipUtil.java b/nostr-java-base/src/main/java/nostr/base/NipUtil.java similarity index 72% rename from nostr-base/src/main/java/nostr/base/NipUtil.java rename to nostr-java-base/src/main/java/nostr/base/NipUtil.java index aab912f2e..a5c714e8b 100644 --- a/nostr-base/src/main/java/nostr/base/NipUtil.java +++ b/nostr-java-base/src/main/java/nostr/base/NipUtil.java @@ -21,18 +21,19 @@ public static boolean checkSupport(@NonNull Relay relay, @NonNull Field field) { } public static boolean checkSupport(@NonNull Relay relay, IElement element) { - if (element == null) + if (element == null) { return true; + } var nip = 1; if (element instanceof IEvent event) { - var e = element.getClass().getDeclaredAnnotation(Event.class); - nip = (e==null) ? event.getNip() : e.nip(); - } else if(element instanceof ITag) { - var t = element.getClass().getDeclaredAnnotation(Tag.class); - nip = (t==null) ? nip : t.nip(); + var e = element.getClass().getDeclaredAnnotation(Event.class); + nip = (e == null) ? event.getNip() : e.nip(); + } else if (element instanceof ITag) { + var t = element.getClass().getDeclaredAnnotation(Tag.class); + nip = (t == null) ? nip : t.nip(); } - + return relay.getSupportedNips().contains(nip); } diff --git a/nostr-base/src/main/java/nostr/base/PrivateKey.java b/nostr-java-base/src/main/java/nostr/base/PrivateKey.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/PrivateKey.java rename to nostr-java-base/src/main/java/nostr/base/PrivateKey.java diff --git a/nostr-base/src/main/java/nostr/base/ElementAttribute.java b/nostr-java-base/src/main/java/nostr/base/Profile.java similarity index 58% rename from nostr-base/src/main/java/nostr/base/ElementAttribute.java rename to nostr-java-base/src/main/java/nostr/base/Profile.java index e9994fd45..82e8459f5 100644 --- a/nostr-base/src/main/java/nostr/base/ElementAttribute.java +++ b/nostr-java-base/src/main/java/nostr/base/Profile.java @@ -1,24 +1,27 @@ package nostr.base; +import java.net.URL; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; /** * - * @author squirrel + * @author eric */ -@Builder @Data @ToString @EqualsAndHashCode @AllArgsConstructor -public class ElementAttribute { +public abstract class Profile { private final String name; - private final Object value; - private final Integer nip; + + @ToString.Exclude + private String about; + + @ToString.Exclude + private URL picture; } diff --git a/nostr-base/src/main/java/nostr/base/PublicKey.java b/nostr-java-base/src/main/java/nostr/base/PublicKey.java similarity index 97% rename from nostr-base/src/main/java/nostr/base/PublicKey.java rename to nostr-java-base/src/main/java/nostr/base/PublicKey.java index 8ceaa0a56..3472d0d66 100644 --- a/nostr-base/src/main/java/nostr/base/PublicKey.java +++ b/nostr-java-base/src/main/java/nostr/base/PublicKey.java @@ -17,6 +17,5 @@ public PublicKey(byte[] rawData) { public PublicKey(String hexPubKey) { super(KeyType.PUBLIC, NostrUtil.hexToBytes(hexPubKey), Bech32Prefix.NPUB); - } - + } } diff --git a/nostr-base/src/main/java/nostr/base/Relay.java b/nostr-java-base/src/main/java/nostr/base/Relay.java similarity index 61% rename from nostr-base/src/main/java/nostr/base/Relay.java rename to nostr-java-base/src/main/java/nostr/base/Relay.java index 1218f949d..b49781b6e 100644 --- a/nostr-base/src/main/java/nostr/base/Relay.java +++ b/nostr-java-base/src/main/java/nostr/base/Relay.java @@ -1,5 +1,6 @@ package nostr.base; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; @@ -64,64 +65,97 @@ public String getName() { @AllArgsConstructor public static class RelayInformationDocument { + @JsonProperty private String name; + @JsonProperty private String description; + @JsonProperty private String pubkey; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String id; + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private String contact; @Builder.Default + @JsonProperty("supported_nips") + @JsonIgnoreProperties(ignoreUnknown = true) private List supportedNips = new ArrayList<>(); @Builder.Default + @JsonProperty("supported_nip_extensions") + @JsonIgnoreProperties(ignoreUnknown = true) private List supportedNipExtensions = new ArrayList<>(); + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private String software; + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private String version; + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private Limitation limitation; + @JsonProperty("payments_url") private String paymentsUrl; + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private Fees fees; @Data public static class Limitation { @JsonProperty("max_message_length") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxMessageLength; @JsonProperty("max_subscriptions") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxSubscriptions; @JsonProperty("max_filters") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxFilters; @JsonProperty("max_limit") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxLimit; @JsonProperty("max_subid_length") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxSubIdLength; @JsonProperty("min_prefix") + @JsonIgnoreProperties(ignoreUnknown = true) private int minPrefix; @JsonProperty("max_event_tags") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxEventTags; @JsonProperty("max_content_length") + @JsonIgnoreProperties(ignoreUnknown = true) private int maxContentLength; @JsonProperty("min_pow_difficulty") + @JsonIgnoreProperties(ignoreUnknown = true) private int minPowDifficulty; @JsonProperty("auth_required") + @JsonIgnoreProperties(ignoreUnknown = true) private boolean authRequired; @JsonProperty("payment_required") + @JsonIgnoreProperties(ignoreUnknown = true) private boolean paymentRequired; } @@ -129,23 +163,37 @@ public static class Limitation { @Data public static class Fees { + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private List admission; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private List publication; @Data public static class AdmissionFee { + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private int amount; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private String unit; } @Data public static class PublicationFee { + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private int amount; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) private String unit; } } - } } diff --git a/nostr-base/src/main/java/nostr/base/Signature.java b/nostr-java-base/src/main/java/nostr/base/Signature.java similarity index 53% rename from nostr-base/src/main/java/nostr/base/Signature.java rename to nostr-java-base/src/main/java/nostr/base/Signature.java index f0b2c8fad..c8c8dfa5c 100644 --- a/nostr-base/src/main/java/nostr/base/Signature.java +++ b/nostr-java-base/src/main/java/nostr/base/Signature.java @@ -1,28 +1,27 @@ package nostr.base; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import nostr.util.NostrUtil; import com.fasterxml.jackson.annotation.JsonValue; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; -import lombok.NonNull; +import lombok.NoArgsConstructor; /** * * @author squirrel */ @Data -@Builder -@AllArgsConstructor +@NoArgsConstructor public class Signature { - @NonNull - private final byte[] rawData; - - @NonNull - private final PublicKey pubKey; + @JsonProperty("rawData") + private byte[] rawData; + + @JsonIgnore + private PublicKey pubKey; @JsonValue @Override diff --git a/nostr-base/src/main/java/nostr/base/Profile.java b/nostr-java-base/src/main/java/nostr/base/UserProfile.java similarity index 61% rename from nostr-base/src/main/java/nostr/base/Profile.java rename to nostr-java-base/src/main/java/nostr/base/UserProfile.java index 98b4b6e95..caac4f1d0 100644 --- a/nostr-base/src/main/java/nostr/base/Profile.java +++ b/nostr-java-base/src/main/java/nostr/base/UserProfile.java @@ -1,13 +1,13 @@ package nostr.base; +import java.net.URL; import nostr.crypto.bech32.Bech32; import nostr.crypto.bech32.Bech32Prefix; -import java.net.URL; import java.util.logging.Level; -import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; import lombok.ToString; import lombok.extern.java.Log; import nostr.util.NostrException; @@ -16,28 +16,24 @@ * * @author squirrel */ -@Builder @Data -@ToString -@EqualsAndHashCode +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) @Log -public final class Profile implements IBech32Encodable { - - private final String name; +public final class UserProfile extends Profile implements IBech32Encodable { - @ToString.Exclude private final PublicKey publicKey; + private final String nip05; - private String about; - - @ToString.Exclude - private URL picture; - - private String nip05; + public UserProfile(@NonNull PublicKey publicKey, String name, String nip05, String about, URL picture) { + super(name, about, picture); + this.publicKey = publicKey; + this.nip05 = nip05; + } @Override public String toBech32() { - try { + try { return Bech32.encode(Bech32.Encoding.BECH32, Bech32Prefix.NPROFILE.getCode(), this.publicKey.getRawData()); } catch (NostrException ex) { log.log(Level.SEVERE, null, ex); diff --git a/nostr-base/src/main/java/nostr/base/annotation/DefaultHandler.java b/nostr-java-base/src/main/java/nostr/base/annotation/DefaultHandler.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/annotation/DefaultHandler.java rename to nostr-java-base/src/main/java/nostr/base/annotation/DefaultHandler.java diff --git a/nostr-base/src/main/java/nostr/base/annotation/Event.java b/nostr-java-base/src/main/java/nostr/base/annotation/Event.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/annotation/Event.java rename to nostr-java-base/src/main/java/nostr/base/annotation/Event.java diff --git a/nostr-base/src/main/java/nostr/base/annotation/JsonList.java b/nostr-java-base/src/main/java/nostr/base/annotation/JsonList.java similarity index 91% rename from nostr-base/src/main/java/nostr/base/annotation/JsonList.java rename to nostr-java-base/src/main/java/nostr/base/annotation/JsonList.java index 7a54f9b09..32a4bebf0 100644 --- a/nostr-base/src/main/java/nostr/base/annotation/JsonList.java +++ b/nostr-java-base/src/main/java/nostr/base/annotation/JsonList.java @@ -12,6 +12,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@Deprecated(forRemoval = true) public @interface JsonList { } diff --git a/nostr-base/src/main/java/nostr/base/annotation/JsonString.java b/nostr-java-base/src/main/java/nostr/base/annotation/JsonString.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/annotation/JsonString.java rename to nostr-java-base/src/main/java/nostr/base/annotation/JsonString.java diff --git a/nostr-base/src/main/java/nostr/base/annotation/Key.java b/nostr-java-base/src/main/java/nostr/base/annotation/Key.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/annotation/Key.java rename to nostr-java-base/src/main/java/nostr/base/annotation/Key.java diff --git a/nostr-base/src/main/java/nostr/base/annotation/Tag.java b/nostr-java-base/src/main/java/nostr/base/annotation/Tag.java similarity index 100% rename from nostr-base/src/main/java/nostr/base/annotation/Tag.java rename to nostr-java-base/src/main/java/nostr/base/annotation/Tag.java diff --git a/nostr-java-base/src/main/resources/app.properties b/nostr-java-base/src/main/resources/app.properties new file mode 100644 index 000000000..7090dc107 --- /dev/null +++ b/nostr-java-base/src/main/resources/app.properties @@ -0,0 +1,4 @@ +profile=profile.properties +client=client.properties +relays=relays.properties +config.folder=.nostr-java \ No newline at end of file diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml new file mode 100644 index 000000000..f28c19cc3 --- /dev/null +++ b/nostr-java-client/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + nostr-java + nostr-java + 0.2 + + nostr-java-client + jar + + + ${project.groupId} + nostr-java-event + ${project.version} + + + ${project.groupId} + nostr-java-base + ${project.version} + + + ${project.groupId} + nostr-java-id + ${project.version} + + + ${project.groupId} + nostr-java-ws-handler-interface + ${project.version} + + + ${project.groupId} + nostr-java-ws-request-handler-provider + ${project.version} + + + ${project.groupId} + nostr-java-ws + ${project.version} + + + + nostr.client.NostrJavaClient + + \ No newline at end of file diff --git a/nostr-id/src/main/java/module-info.java b/nostr-java-client/src/main/java/module-info.java similarity index 78% rename from nostr-id/src/main/java/module-info.java rename to nostr-java-client/src/main/java/module-info.java index 4608068bf..b2bb69370 100644 --- a/nostr-id/src/main/java/module-info.java +++ b/nostr-java-client/src/main/java/module-info.java @@ -1,16 +1,20 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/module-info.java to edit this template + */ -module nostr.id { - requires static lombok; - requires nostr.base; - requires nostr.crypto; +module nostr.client { requires nostr.event; + requires static lombok; + requires java.logging; requires nostr.util; - requires nostr.ws; - requires nostr.ws.handler; - requires nostr.ws.request.handler.provider; + requires nostr.base; requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.core; + requires nostr.crypto; + requires nostr.id; + requires nostr.ws; requires org.eclipse.jetty.websocket.jetty.client; requires org.eclipse.jetty.websocket.jetty.api; requires org.eclipse.jetty.websocket.jetty.common; @@ -21,15 +25,16 @@ requires org.eclipse.jetty.util; requires org.eclipse.jetty.io; requires org.slf4j; - requires org.eclipse.jetty.alpn.client; - requires org.eclipse.jetty.alpn.java.client; requires org.eclipse.jetty.http2.client; requires org.eclipse.jetty.http2.common; requires org.eclipse.jetty.http2.hpack; + requires org.eclipse.jetty.alpn.client; requires org.eclipse.jetty.http2.http.client.transport; + requires org.eclipse.jetty.alpn.java.client; + requires nostr.ws.response.handler.provider; requires org.bouncycastle.provider; - requires java.logging; - requires java.desktop; + requires nostr.ws.handler; + requires nostr.ws.request.handler.provider; - exports nostr.id; + exports nostr.client; } diff --git a/nostr-id/src/main/java/nostr/id/Client.java b/nostr-java-client/src/main/java/nostr/client/Client.java similarity index 77% rename from nostr-id/src/main/java/nostr/id/Client.java rename to nostr-java-client/src/main/java/nostr/client/Client.java index f63ec723b..b7b6d887a 100644 --- a/nostr-id/src/main/java/nostr/id/Client.java +++ b/nostr-java-client/src/main/java/nostr/client/Client.java @@ -1,4 +1,4 @@ -package nostr.id; +package nostr.client; import java.io.IOException; import java.net.URI; @@ -13,18 +13,17 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import lombok.Data; import lombok.NonNull; import lombok.extern.java.Log; -import nostr.base.BaseConfiguration; +import nostr.util.AbstractBaseConfiguration; import nostr.base.Relay; +import nostr.event.BaseMessage; import nostr.event.impl.ClientAuthenticationEvent; -import nostr.event.impl.GenericEvent; -import nostr.event.impl.GenericMessage; -import nostr.event.message.AuthMessage; +import nostr.event.message.ClientAuthenticationMessage; +import nostr.id.Identity; import nostr.util.NostrException; import nostr.ws.Connection; import nostr.ws.handler.spi.IRequestHandler; @@ -44,11 +43,11 @@ public class Client { private final ThreadPoolExecutor threadPool; private IRequestHandler requestHandler; - private Client(String relayConfFile) throws IOException { + private Client() throws IOException { this.futureRelays = new HashSet<>(); this.requestHandler = new DefaultRequestHandler(); this.threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(); - this.init(relayConfFile); + this.init(); } private Client(Map relays) { @@ -58,10 +57,10 @@ private Client(Map relays) { this.init(relays); } - public static Client getInstance(String relayConfFile) { + public static Client getInstance() { if (INSTANCE == null) { try { - INSTANCE = new Client(relayConfFile); + INSTANCE = new Client(); } catch (IOException ex) { log.log(Level.SEVERE, null, ex); throw new RuntimeException(ex); @@ -100,7 +99,7 @@ public Set getRelays() { }).collect(Collectors.toSet()); } - public void send(@NonNull GenericMessage message) { + public void send(@NonNull BaseMessage message) { futureRelays.parallelStream() .filter(fr -> { try { @@ -124,8 +123,18 @@ public void auth(Identity identity, String challenge) throws NostrException { log.log(Level.INFO, "Authenticating..."); Set relays = getRelaySet(); - GenericEvent event = new ClientAuthenticationEvent(identity.getPublicKey(), challenge, relays); - GenericMessage authMsg = new AuthMessage(event); + var event = new ClientAuthenticationEvent(identity.getPublicKey(), challenge, relays); + BaseMessage authMsg = new ClientAuthenticationMessage(event); + + identity.sign(event); + this.send(authMsg); + } + + public void auth(Identity identity, String challenge, Relay relay) throws NostrException { + + log.log(Level.INFO, "Authenticating..."); + var event = new ClientAuthenticationEvent(identity.getPublicKey(), challenge, relay); + BaseMessage authMsg = new ClientAuthenticationMessage(event); identity.sign(event); this.send(authMsg); @@ -138,7 +147,7 @@ private Set getRelaySet() { try { result.add(fr.get()); } catch (InterruptedException | ExecutionException ex) { - log.log(Level.SEVERE, null, ex); + throw new RuntimeException(ex); } }); @@ -168,13 +177,13 @@ private void init(Map mapRelays) { }); } - private void init(String file) throws IOException { - this.init(toMapRelays(file)); + private void init() throws IOException { + this.init(toMapRelays()); } - private Map toMapRelays(String file) throws IOException { + private Map toMapRelays() throws IOException { Map relays = new HashMap<>(); - List relayList = new RelayConfiguration(file).getRelays(); + List relayList = new RelayConfiguration().getRelays(); relayList.stream().forEach(r -> relays.put(r.getName(), r.getUri())); return relays; } @@ -189,14 +198,13 @@ private void updateRelayInformation(@NonNull Relay relay) { } @Log - static class RelayConfiguration extends BaseConfiguration { - + static class RelayConfiguration extends AbstractBaseConfiguration { + RelayConfiguration() throws IOException { - this("/relays.properties"); - } - - RelayConfiguration(String file) throws IOException { - super(file); + super(); + var configFile = appConfig.getRelaysProperties(); + configFile = configFile.startsWith("/") ? configFile : "/" + configFile; + load(configFile); } List getRelays() { diff --git a/nostr-crypto/nb-configuration.xml b/nostr-java-crypto/nb-configuration.xml similarity index 100% rename from nostr-crypto/nb-configuration.xml rename to nostr-java-crypto/nb-configuration.xml diff --git a/nostr-crypto/pom.xml b/nostr-java-crypto/pom.xml similarity index 83% rename from nostr-crypto/pom.xml rename to nostr-java-crypto/pom.xml index 70172687c..5b0eadc1f 100644 --- a/nostr-crypto/pom.xml +++ b/nostr-java-crypto/pom.xml @@ -6,12 +6,12 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-crypto + nostr-java-crypto jar - nostr-crypto + A simple Java implementation (no external libs) of Sipa's Python reference implementation test vectors for BIP340 Schnorr signatures for secp256k1. Inspired/Copied from: @@ -25,11 +25,15 @@ org.projectlombok lombok + + org.bouncycastle + bcprov-jdk15on + ${project.groupId} - nostr-util + nostr-java-util ${project.version} diff --git a/nostr-crypto/src/main/java/module-info.java b/nostr-java-crypto/src/main/java/module-info.java similarity index 81% rename from nostr-crypto/src/main/java/module-info.java rename to nostr-java-crypto/src/main/java/module-info.java index d9c79d8e2..297ec0b3c 100644 --- a/nostr-crypto/src/main/java/module-info.java +++ b/nostr-java-crypto/src/main/java/module-info.java @@ -3,6 +3,7 @@ requires java.logging; requires nostr.util; requires static lombok; + requires org.bouncycastle.provider; exports nostr.crypto.bech32; exports nostr.crypto.schnorr; diff --git a/nostr-crypto/src/main/java/nostr/crypto/Pair.java b/nostr-java-crypto/src/main/java/nostr/crypto/Pair.java similarity index 100% rename from nostr-crypto/src/main/java/nostr/crypto/Pair.java rename to nostr-java-crypto/src/main/java/nostr/crypto/Pair.java diff --git a/nostr-crypto/src/main/java/nostr/crypto/Point.java b/nostr-java-crypto/src/main/java/nostr/crypto/Point.java similarity index 100% rename from nostr-crypto/src/main/java/nostr/crypto/Point.java rename to nostr-java-crypto/src/main/java/nostr/crypto/Point.java diff --git a/nostr-crypto/src/main/java/nostr/crypto/bech32/Bech32.java b/nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32.java similarity index 100% rename from nostr-crypto/src/main/java/nostr/crypto/bech32/Bech32.java rename to nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32.java diff --git a/nostr-crypto/src/main/java/nostr/crypto/bech32/Bech32Prefix.java b/nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32Prefix.java similarity index 100% rename from nostr-crypto/src/main/java/nostr/crypto/bech32/Bech32Prefix.java rename to nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32Prefix.java diff --git a/nostr-crypto/src/main/java/nostr/crypto/schnorr/Schnorr.java b/nostr-java-crypto/src/main/java/nostr/crypto/schnorr/Schnorr.java similarity index 79% rename from nostr-crypto/src/main/java/nostr/crypto/schnorr/Schnorr.java rename to nostr-java-crypto/src/main/java/nostr/crypto/schnorr/Schnorr.java index f056161c1..5c9c4eae1 100644 --- a/nostr-crypto/src/main/java/nostr/crypto/schnorr/Schnorr.java +++ b/nostr-java-crypto/src/main/java/nostr/crypto/schnorr/Schnorr.java @@ -1,11 +1,18 @@ package nostr.crypto.schnorr; import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; +import java.security.Security; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECGenParameterSpec; import java.util.Arrays; -import java.util.logging.Level; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; import lombok.extern.java.Log; import nostr.crypto.Point; @@ -14,12 +21,6 @@ @Log public class Schnorr { - private static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG"; - private static final String RANDOM_NUMBER_ALGORITHM_PROVIDER = "SUN"; - - private static final BigInteger MAXPRIVATEKEY - = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", 16); - /** * * @param msg @@ -119,26 +120,17 @@ public static boolean verify(byte[] msg, byte[] pubkey, byte[] sig) throws Excep * @return */ public static byte[] generatePrivateKey() { - SecureRandom secureRandom; try { - secureRandom = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM, RANDOM_NUMBER_ALGORITHM_PROVIDER); - } catch (NoSuchAlgorithmException | NoSuchProviderException e) { - log.log(Level.SEVERE, null, e); - secureRandom = new SecureRandom(); + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDSA", "BC"); + kpg.initialize(new ECGenParameterSpec("secp256k1"), SecureRandom.getInstanceStrong()); + KeyPair processorKeyPair = kpg.genKeyPair(); + + return NostrUtil.bytesFromBigInteger(((ECPrivateKey) processorKeyPair.getPrivate()).getS()); + + } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException e) { + throw new RuntimeException(e); } - - // Generate the key, skipping as many as desired. - byte[] privateKeyAttempt = new byte[32]; - secureRandom.nextBytes(privateKeyAttempt); - BigInteger privateKeyCheck = new BigInteger(1, privateKeyAttempt); - - while (privateKeyCheck.compareTo(BigInteger.ZERO) == 0 - || privateKeyCheck.compareTo(MAXPRIVATEKEY) == 1) { - secureRandom.nextBytes(privateKeyAttempt); - privateKeyCheck = new BigInteger(1, privateKeyAttempt); - } - - return privateKeyAttempt; } public static byte[] genPubKey(byte[] secKey) throws Exception { diff --git a/nostr-java-event/README.md b/nostr-java-event/README.md new file mode 100644 index 000000000..9aa10920e --- /dev/null +++ b/nostr-java-event/README.md @@ -0,0 +1,24 @@ +# nostr-event + +We offer support for selected events and tags out of the box: + +The events: +- Event Deletion +- Encrypted Direct Messages +- Ephemeral Events +- Internet Identifier Metadata Events +- Mentions +- Set Metadata events +- Ots Events +- Reaction Events +- Replaceable Events +- Text Note Events + +The Tags: +- Deletion Tag +- Event Tag +- Nonce Tag +- Pubkey Tag +- Subject Tag + +Additionally, you may use the `GenericTag` and `GenericEvent` classes to create your custom tags and events. diff --git a/nostr-event/nb-configuration.xml b/nostr-java-event/nb-configuration.xml similarity index 100% rename from nostr-event/nb-configuration.xml rename to nostr-java-event/nb-configuration.xml diff --git a/nostr-event/pom.xml b/nostr-java-event/pom.xml similarity index 84% rename from nostr-event/pom.xml rename to nostr-java-event/pom.xml index 881acb42e..1861de391 100644 --- a/nostr-event/pom.xml +++ b/nostr-java-event/pom.xml @@ -6,10 +6,10 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-event + nostr-java-event jar @@ -22,12 +22,12 @@ ${project.groupId} - nostr-base + nostr-java-base ${project.version} ${project.groupId} - nostr-util + nostr-java-util ${project.version} diff --git a/nostr-event/src/main/java/module-info.java b/nostr-java-event/src/main/java/module-info.java similarity index 80% rename from nostr-event/src/main/java/module-info.java rename to nostr-java-event/src/main/java/module-info.java index bc5de80d1..c8ed2e4bc 100644 --- a/nostr-event/src/main/java/module-info.java +++ b/nostr-java-event/src/main/java/module-info.java @@ -13,9 +13,10 @@ exports nostr.event; exports nostr.event.impl; exports nostr.event.list; - exports nostr.event.marshaller.impl; exports nostr.event.message; - exports nostr.event.serializer; + exports nostr.event.json.codec; + exports nostr.event.json.deserializer; + exports nostr.event.json.serializer; exports nostr.event.tag; exports nostr.event.util; } diff --git a/nostr-event/src/main/java/nostr/event/BaseEvent.java b/nostr-java-event/src/main/java/nostr/event/BaseEvent.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/BaseEvent.java rename to nostr-java-event/src/main/java/nostr/event/BaseEvent.java diff --git a/nostr-event/src/main/java/nostr/event/BaseMessage.java b/nostr-java-event/src/main/java/nostr/event/BaseMessage.java similarity index 80% rename from nostr-event/src/main/java/nostr/event/BaseMessage.java rename to nostr-java-event/src/main/java/nostr/event/BaseMessage.java index 40abcc573..b6bc477e0 100644 --- a/nostr-event/src/main/java/nostr/event/BaseMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/BaseMessage.java @@ -1,4 +1,3 @@ - package nostr.event; import nostr.base.IElement; @@ -13,8 +12,13 @@ @Data @AllArgsConstructor @ToString -@Deprecated public abstract class BaseMessage implements IElement { private final String command; + + @Override + public Integer getNip() { + return 1; + } + } diff --git a/nostr-event/src/main/java/nostr/event/BaseTag.java b/nostr-java-event/src/main/java/nostr/event/BaseTag.java similarity index 61% rename from nostr-event/src/main/java/nostr/event/BaseTag.java rename to nostr-java-event/src/main/java/nostr/event/BaseTag.java index 127fe4209..ea7af8687 100644 --- a/nostr-event/src/main/java/nostr/event/BaseTag.java +++ b/nostr-java-event/src/main/java/nostr/event/BaseTag.java @@ -1,6 +1,8 @@ - package nostr.event; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; @@ -9,11 +11,9 @@ import java.util.List; import java.util.logging.Level; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.ToString; import lombok.extern.java.Log; import nostr.base.IEvent; @@ -22,20 +22,24 @@ import nostr.base.Relay; import nostr.base.annotation.Key; import nostr.base.annotation.Tag; +import nostr.event.json.deserializer.TagDeserializer; +import nostr.event.json.serializer.TagSerializer; import nostr.util.NostrException; /** * * @author squirrel */ -@JsonPropertyOrder({"code"}) @Data @ToString @EqualsAndHashCode(callSuper = false) +@NoArgsConstructor @Log +@JsonDeserialize(using = TagDeserializer.class) +@JsonSerialize(using = TagSerializer.class) public abstract class BaseTag implements ITag { - @JsonIgnore + @JsonIgnore private IEvent parent; @Override @@ -50,43 +54,21 @@ public String getCode() { } @Override - public String printAttributes(Relay relay, boolean escape) throws NostrException { - - var fields = this.getClass().getDeclaredFields(); - var index = 0; - var result = new StringBuilder(); - var fieldList = getSupportedFields(fields, relay); - - if (fieldList.size() >= 1) { - result.append(","); - - for (Field f : fieldList) { - final String fieldValue = getFieldValue(f); - - if (!escape) { - result.append("\""); - } else { - result.append("\\\""); - } - - result.append(fieldValue); - - if (!escape) { - result.append("\""); - } else { - result.append("\\\""); - } + public Integer getNip() { + return 1; + } - if (++index < fieldList.size()) { - result.append(","); - } - } + public String getFieldValue(Field field) throws NostrException { + try { + Object f = new PropertyDescriptor(field.getName(), this.getClass()).getReadMethod().invoke(this); + return f != null ? f.toString() : null; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | IntrospectionException ex) { + throw new NostrException(ex); } - - return result.toString(); } - private List getSupportedFields(Field[] fields, Relay relay) throws NostrException { + public List getSupportedFields(Relay relay) throws NostrException { + var fields = this.getClass().getDeclaredFields(); List fieldList = new ArrayList<>(); for (Field f : fields) { if (nipSupport(f, relay) && null != getFieldValue(f)) { @@ -110,13 +92,4 @@ private boolean nipSupport(Field field, Relay relay) { return NipUtil.checkSupport(relay, field); } - - private String getFieldValue(Field field) throws NostrException { - try { - Object f = new PropertyDescriptor(field.getName(), this.getClass()).getReadMethod().invoke(this); - return f != null ? f.toString() : null; - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | IntrospectionException ex) { - throw new NostrException(ex); - } - } } diff --git a/nostr-event/src/main/java/nostr/event/Kind.java b/nostr-java-event/src/main/java/nostr/event/Kind.java similarity index 94% rename from nostr-event/src/main/java/nostr/event/Kind.java rename to nostr-java-event/src/main/java/nostr/event/Kind.java index 0b98010b1..7c0eb67da 100644 --- a/nostr-event/src/main/java/nostr/event/Kind.java +++ b/nostr-java-event/src/main/java/nostr/event/Kind.java @@ -18,6 +18,7 @@ public enum Kind { CONTACT_LIST(3, "contact_list"), ENCRYPTED_DIRECT_MESSAGE(4, "encrypted_direct_message"), DELETION(5, "deletion"), + REPOST(6,"repost"), REACTION(7, "reaction"), CHANNEL_CREATE(40, "channel_create"), CHANNEL_METADATA(41, "channel_metadata"), diff --git a/nostr-event/src/main/java/nostr/event/Marker.java b/nostr-java-event/src/main/java/nostr/event/Marker.java similarity index 92% rename from nostr-event/src/main/java/nostr/event/Marker.java rename to nostr-java-event/src/main/java/nostr/event/Marker.java index 426c127f8..dbc272e3a 100644 --- a/nostr-event/src/main/java/nostr/event/Marker.java +++ b/nostr-java-event/src/main/java/nostr/event/Marker.java @@ -19,4 +19,4 @@ public enum Marker { public String getValue() { return value; } -} +} \ No newline at end of file diff --git a/nostr-java-event/src/main/java/nostr/event/Nip05Content.java b/nostr-java-event/src/main/java/nostr/event/Nip05Content.java new file mode 100644 index 000000000..e43cb16b5 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/Nip05Content.java @@ -0,0 +1,29 @@ +package nostr.event; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import lombok.Data; +import lombok.NoArgsConstructor; +import nostr.base.IElement; + +/** + * + * @author eric + */ +@Data +@NoArgsConstructor +public class Nip05Content implements IElement { + + @JsonProperty("names") + private Map names; + + @JsonProperty("relays") + private Map> relays; + + @Override + public Integer getNip() { + return 1; + } + +} diff --git a/nostr-event/src/main/java/nostr/event/Reaction.java b/nostr-java-event/src/main/java/nostr/event/Reaction.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/Reaction.java rename to nostr-java-event/src/main/java/nostr/event/Reaction.java diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ChannelCreateEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ChannelCreateEvent.java new file mode 100644 index 000000000..cff851d61 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ChannelCreateEvent.java @@ -0,0 +1,45 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import lombok.NonNull; +import nostr.base.ChannelProfile; +import nostr.base.PublicKey; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; +import static nostr.event.impl.GenericEvent.escapeJsonString; + +/** + * @author guilhermegps + * + */ +@Event(name = "Create Channel", nip = 28) +public class ChannelCreateEvent extends GenericEvent { + + public ChannelCreateEvent(@NonNull PublicKey pubKey, @NonNull List tags, String content) { + super(pubKey, Kind.CHANNEL_CREATE, tags, content); + } + + public ChannelCreateEvent(@NonNull PublicKey pubKey, @NonNull List tags, ChannelProfile profile) { + super(pubKey, Kind.CHANNEL_CREATE, tags); + this.setContent(profile); + } + + private void setContent(ChannelProfile profile) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonString = objectMapper.writeValueAsString(profile); + + // Escape the JSON string + String escapedJsonString = escapeJsonString(jsonString); + + this.setContent(escapedJsonString); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/nostr-event/src/main/java/nostr/event/impl/ChannelMessageEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ChannelMessageEvent.java similarity index 54% rename from nostr-event/src/main/java/nostr/event/impl/ChannelMessageEvent.java rename to nostr-java-event/src/main/java/nostr/event/impl/ChannelMessageEvent.java index 529e4ae8f..db56d4943 100644 --- a/nostr-event/src/main/java/nostr/event/impl/ChannelMessageEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/ChannelMessageEvent.java @@ -1,10 +1,11 @@ package nostr.event.impl; +import java.util.List; import lombok.NonNull; import nostr.base.PublicKey; import nostr.base.annotation.Event; +import nostr.event.BaseTag; import nostr.event.Kind; -import nostr.event.list.TagList; /** * @author guilhermegps @@ -13,8 +14,7 @@ @Event(name = "Channel Message", nip = 28) public class ChannelMessageEvent extends GenericEvent { - public ChannelMessageEvent(@NonNull PublicKey pubKey, @NonNull TagList tags, String content) { - super(pubKey, Kind.CHANNEL_MESSAGE, tags, content); - } - + public ChannelMessageEvent(@NonNull PublicKey pubKey, @NonNull List tags, String content) { + super(pubKey, Kind.CHANNEL_MESSAGE, tags, content); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ChannelMetadataEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ChannelMetadataEvent.java new file mode 100644 index 000000000..b19405814 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ChannelMetadataEvent.java @@ -0,0 +1,43 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import lombok.NonNull; +import nostr.base.ChannelProfile; +import nostr.base.PublicKey; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; + +/** + * @author guilhermegps + * + */ +@Event(name = "Channel Metadata", nip = 28) +public class ChannelMetadataEvent extends GenericEvent { + + public ChannelMetadataEvent(@NonNull PublicKey pubKey, @NonNull List tags, String content) { + super(pubKey, Kind.CHANNEL_METADATA, tags, content); + } + + public ChannelMetadataEvent(@NonNull PublicKey pubKey, @NonNull List tags, ChannelProfile profile) { + super(pubKey, Kind.CHANNEL_METADATA, tags); + this.setContent(profile); + } + + private void setContent(ChannelProfile profile) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonString = objectMapper.writeValueAsString(profile); + + // Escape the JSON string + String escapedJsonString = escapeJsonString(jsonString); + + this.setContent(escapedJsonString); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ClientAuthenticationEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ClientAuthenticationEvent.java new file mode 100644 index 000000000..0c081410e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ClientAuthenticationEvent.java @@ -0,0 +1,84 @@ +package nostr.event.impl; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import lombok.NonNull; +import nostr.base.ElementAttribute; +import nostr.base.ITag; +import nostr.base.PublicKey; +import nostr.base.Relay; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; + +/** + * + * @author squirrel + */ +@Event(name = "Authentication of clients to relays", nip = 42) +public class ClientAuthenticationEvent extends GenericEvent { + + public ClientAuthenticationEvent(@NonNull PublicKey pubKey, @NonNull List tags) { + super(pubKey, Kind.CLIENT_AUTH, tags); + } + + public ClientAuthenticationEvent(@NonNull PublicKey pubKey, String challenge, @NonNull Set relays) { + super(pubKey, Kind.CLIENT_AUTH); + + Set chAttributes = new HashSet<>(); + var attribute = ElementAttribute.builder().nip(42).name("challenge").value(challenge).build(); + chAttributes.add(attribute); + + this.setTags(new ArrayList<>()); + ITag chTag = new GenericTag("auth", 42, chAttributes); + + this.addTag((GenericTag) chTag); + + relays.stream().forEach(r -> { + try { + final Set relayAttributes = new HashSet<>(); + final ElementAttribute relayAttribute = getRelayAttribute(r); + relayAttributes.add(relayAttribute); + final ITag relayTag = new GenericTag("relay", 42, relayAttributes); + this.addTag((BaseTag) relayTag); + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException(ex); + } + }); + + this.setNip(42); + } + + public ClientAuthenticationEvent(@NonNull PublicKey pubKey, String challenge, @NonNull Relay relay) { + super(pubKey, Kind.CLIENT_AUTH); + + try { + Set chAttributes = new HashSet<>(); + var attribute = ElementAttribute.builder().nip(42).name("challenge").value(challenge).build(); + chAttributes.add(attribute); + + this.setTags(new ArrayList<>()); + ITag chTag = new GenericTag("auth", 42, chAttributes); + + this.addTag((BaseTag) chTag); + + final Set relayAttributes = new HashSet<>(); + final ElementAttribute relayAttribute = getRelayAttribute(relay); + relayAttributes.add(relayAttribute); + final ITag relayTag = new GenericTag("relay", 42, relayAttributes); + this.addTag((BaseTag) relayTag); + + this.setNip(42); + } catch (ExecutionException | InterruptedException ex) { + throw new RuntimeException(ex); + } + } + + private static ElementAttribute getRelayAttribute(Relay relay) throws ExecutionException, InterruptedException { + return ElementAttribute.builder().nip(42).name("uri").value(relay.getUri()).build(); + } + +} diff --git a/nostr-event/src/main/java/nostr/event/impl/DeletionEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/DeletionEvent.java similarity index 50% rename from nostr-event/src/main/java/nostr/event/impl/DeletionEvent.java rename to nostr-java-event/src/main/java/nostr/event/impl/DeletionEvent.java index 12e4aaf4b..56d0f363f 100644 --- a/nostr-event/src/main/java/nostr/event/impl/DeletionEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/DeletionEvent.java @@ -1,12 +1,13 @@ package nostr.event.impl; +import java.util.List; import nostr.event.Kind; import nostr.base.PublicKey; import lombok.Data; import lombok.EqualsAndHashCode; import nostr.base.annotation.Event; -import nostr.event.list.TagList; +import nostr.event.BaseTag; /** * @@ -17,11 +18,11 @@ @Event(name = "Event Deletion", nip = 9) public class DeletionEvent extends GenericEvent { - public DeletionEvent(PublicKey pubKey, TagList tagList, String content) { - super(pubKey, Kind.DELETION, tagList, content); + public DeletionEvent(PublicKey pubKey, List tags, String content) { + super(pubKey, Kind.DELETION, tags, content); } - public DeletionEvent(PublicKey pubKey, TagList tagList) { - this(pubKey, tagList, "Deletion request"); + public DeletionEvent(PublicKey pubKey, List tags) { + this(pubKey, tags, "Deletion request"); } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/DirectMessageEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/DirectMessageEvent.java new file mode 100644 index 000000000..446704751 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/DirectMessageEvent.java @@ -0,0 +1,26 @@ +package nostr.event.impl; + +import java.util.List; +import nostr.base.PublicKey; +import nostr.event.Kind; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.tag.PubKeyTag; + +/** + * + * @author squirrel + */ +@Event(name = "Encrypted Direct Message", nip = 4) +public class DirectMessageEvent extends GenericEvent { + + public DirectMessageEvent(PublicKey sender, List tags, String content) { + super(sender, Kind.ENCRYPTED_DIRECT_MESSAGE, tags, content); + } + + public DirectMessageEvent(PublicKey sender, PublicKey recipient, String content) { + super(sender, Kind.ENCRYPTED_DIRECT_MESSAGE); + this.setContent(content); + this.addTag(PubKeyTag.builder().publicKey(recipient).build()); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/EphemeralEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/EphemeralEvent.java new file mode 100644 index 000000000..f25242ed1 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/EphemeralEvent.java @@ -0,0 +1,38 @@ +package nostr.event.impl; + +import java.util.ArrayList; +import java.util.List; +import nostr.event.Kind; +import nostr.base.PublicKey; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.java.Log; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.tag.PubKeyTag; + +/** + * + * @author squirrel + */ +@Data +@Log +@EqualsAndHashCode(callSuper = false) +@Event(name = "Ephemeral Events", nip = 16) +public class EphemeralEvent extends GenericEvent { + + public EphemeralEvent(PublicKey pubKey, Integer kind, List tags, String content) { + super(pubKey, kind, tags, content); + } + + public EphemeralEvent(PublicKey pubKey, Integer kind, List tags) { + this(pubKey, kind, tags, "..."); + } + + public EphemeralEvent(PublicKey sender, Integer kind, PublicKey recipient) { + this(sender, kind, new ArrayList<>()); + this.addTag(PubKeyTag.builder().publicKey(recipient).build()); + } + + // TODO - Validate the kind. +} diff --git a/nostr-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java similarity index 74% rename from nostr-event/src/main/java/nostr/event/impl/Filters.java rename to nostr-java-event/src/main/java/nostr/event/impl/Filters.java index 40a3bf7a5..543965287 100644 --- a/nostr-event/src/main/java/nostr/event/impl/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Builder; @@ -10,12 +11,13 @@ import lombok.EqualsAndHashCode; import nostr.base.annotation.Key; import nostr.event.BaseEvent; +import nostr.event.json.deserializer.CustomGenericTagQueryListDeserializer; +import nostr.event.json.serializer.CustomGenericTagQueryListSerializer; +import nostr.event.json.serializer.CustomIdEventListSerializer; import nostr.event.list.EventList; import nostr.event.list.GenericTagQueryList; import nostr.event.list.KindList; import nostr.event.list.PublicKeyList; -import nostr.event.serializer.CustomGenericTagListSerializer; -import nostr.event.serializer.CustomIdEventListSerializer; /** * @@ -57,7 +59,8 @@ public class Filters extends BaseEvent { private Integer limit; @Key(nip = 12) - @JsonSerialize(using=CustomGenericTagListSerializer.class) + @JsonSerialize(using=CustomGenericTagQueryListSerializer.class) + @JsonDeserialize(using=CustomGenericTagQueryListDeserializer.class) private GenericTagQueryList genericTagQueryList; @Override diff --git a/nostr-event/src/main/java/nostr/event/impl/GenericEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java similarity index 64% rename from nostr-event/src/main/java/nostr/event/impl/GenericEvent.java rename to nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java index 8563cad0c..26039eafe 100644 --- a/nostr-event/src/main/java/nostr/event/impl/GenericEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java @@ -1,29 +1,27 @@ package nostr.event.impl; -import java.beans.IntrospectionException; import java.beans.Transient; -import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Level; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import java.util.ArrayList; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.extern.java.Log; import nostr.base.ElementAttribute; +import nostr.base.IEncoder; import nostr.base.IGenericElement; -import nostr.base.IMarshaller; import nostr.base.ISignable; import nostr.base.ITag; import nostr.base.PublicKey; @@ -33,8 +31,10 @@ import nostr.crypto.bech32.Bech32; import nostr.crypto.bech32.Bech32Prefix; import nostr.event.BaseEvent; +import nostr.event.BaseTag; import nostr.event.Kind; -import nostr.event.list.TagList; +import nostr.event.json.deserializer.PublicKeyDeserializer; +import nostr.event.json.deserializer.SignatureDeserializer; import nostr.util.NostrException; import nostr.util.NostrUtil; @@ -55,6 +55,7 @@ public class GenericEvent extends BaseEvent implements ISignable, IGenericElemen @JsonProperty("pubkey") @EqualsAndHashCode.Include @JsonString + @JsonDeserialize(using = PublicKeyDeserializer.class) private PublicKey pubKey; @Key @@ -68,7 +69,8 @@ public class GenericEvent extends BaseEvent implements ISignable, IGenericElemen @Key @EqualsAndHashCode.Exclude - private TagList tags; + @JsonProperty("tags") + private List tags; @Key @EqualsAndHashCode.Exclude @@ -78,6 +80,7 @@ public class GenericEvent extends BaseEvent implements ISignable, IGenericElemen @JsonProperty("sig") @EqualsAndHashCode.Exclude @JsonString + @JsonDeserialize(using = SignatureDeserializer.class) private Signature signature; @JsonIgnore @@ -91,24 +94,24 @@ public class GenericEvent extends BaseEvent implements ISignable, IGenericElemen @JsonIgnore @EqualsAndHashCode.Exclude private final Set attributes; - + public GenericEvent() { this.attributes = new HashSet<>(); - } + } public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind) { - this(pubKey, kind, new TagList(), null); + this(pubKey, kind, new ArrayList<>(), null); } - public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull TagList tags) { + public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull List tags) { this(pubKey, kind, tags, null); } - public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull TagList tags, String content) { + public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull List tags, String content) { this(pubKey, kind.getValue(), tags, content); } - public GenericEvent(@NonNull PublicKey pubKey, @NonNull Integer kind, @NonNull TagList tags, String content) { + public GenericEvent(@NonNull PublicKey pubKey, @NonNull Integer kind, @NonNull List tags, String content) { this.pubKey = pubKey; this.kind = kind; this.tags = tags; @@ -122,45 +125,46 @@ public GenericEvent(@NonNull PublicKey pubKey, @NonNull Integer kind, @NonNull T @Override public String toBech32() { if (!isSigned()) { - try { - this.update(); - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - log.log(Level.SEVERE, null, ex); - throw new RuntimeException(ex); - } + this.update(); } + try { return Bech32.toBech32(Bech32Prefix.NOTE, this.getId()); } catch (NostrException ex) { - log.log(Level.SEVERE, null, ex); throw new RuntimeException(ex); } } - public void setTags(TagList tags) { + public void setTags(List tags) { this.tags = tags; - for (Object o : tags.getList()) { + for (Object o : tags) { ((ITag) o).setParent(this); } } - public void addTag(ITag tag) { - List list = tags.getList(); + public void addTag(BaseTag tag) { - if (!list.contains(tag)) { + if (!tags.contains(tag)) { tag.setParent(this); - list.add(tag); + ((List) tags).add(tag); } } - public void update() throws NoSuchAlgorithmException, IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException, NostrException { - this.createdAt = Instant.now().getEpochSecond(); + public void update() { + + try { + this.validate(); + + this.createdAt = Instant.now().getEpochSecond(); - this._serializedEvent = this.serialize().getBytes(StandardCharsets.UTF_8); + this._serializedEvent = this.serialize().getBytes(StandardCharsets.UTF_8); - this.id = NostrUtil.bytesToHex(NostrUtil.sha256(_serializedEvent)); + this.id = NostrUtil.bytesToHex(NostrUtil.sha256(_serializedEvent)); + } catch (NostrException | NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } } @Transient @@ -173,9 +177,23 @@ public void addAttribute(ElementAttribute attribute) { this.attributes.add(attribute); } + protected void validate() { + + } + + protected static String escapeJsonString(String jsonString) { + return jsonString.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\b", "\\b") + .replace("\f", "\\f") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + @SuppressWarnings("unchecked") private String serialize() throws NostrException { - var mapper = IMarshaller.MAPPER; + var mapper = IEncoder.MAPPER; var arrayNode = JsonNodeFactory.instance.arrayNode(); try { @@ -192,9 +210,9 @@ private String serialize() throws NostrException { } } - protected final void updateTagsParents(TagList tagList) { - if (tagList != null && !tagList.getList().isEmpty()) { - for (Object t : tagList.getList()) { + protected final void updateTagsParents(List tagList) { + if (tagList != null && !tagList.isEmpty()) { + for (Object t : tagList) { ITag tag = (ITag) t; tag.setParent(this); } diff --git a/nostr-event/src/main/java/nostr/event/impl/GenericMessage.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java similarity index 68% rename from nostr-event/src/main/java/nostr/event/impl/GenericMessage.java rename to nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java index e8e85c7a0..3e529a6ca 100644 --- a/nostr-event/src/main/java/nostr/event/impl/GenericMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java @@ -1,21 +1,30 @@ package nostr.event.impl; +import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.HashSet; import java.util.Set; import lombok.Data; +import lombok.EqualsAndHashCode; import nostr.base.ElementAttribute; import nostr.base.IElement; import nostr.base.IGenericElement; +import nostr.event.BaseMessage; /** * * @author squirrel */ @Data -public class GenericMessage implements IGenericElement, IElement { +@EqualsAndHashCode(callSuper = false) +public class GenericMessage extends BaseMessage implements IGenericElement, IElement { - private final String command; +// @JsonProperty +// private final String command; + + @JsonIgnore private final Set attributes; + + @JsonIgnore private final Integer nip; public GenericMessage(String command) { @@ -27,7 +36,7 @@ public GenericMessage(String command, Integer nip) { } public GenericMessage(String command, Set attributes, Integer nip) { - this.command = command; + super(command); this.attributes = attributes; this.nip = nip; } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java new file mode 100644 index 000000000..5211a965e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java @@ -0,0 +1,47 @@ +package nostr.event.impl; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.java.Log; +import nostr.base.ElementAttribute; +import nostr.base.IGenericElement; +import nostr.event.BaseTag; + +/** + * + * @author squirrel + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Log +@AllArgsConstructor +public class GenericTag extends BaseTag implements IGenericElement { + + private final String code; + + @JsonIgnore + @EqualsAndHashCode.Exclude + private final Integer nip; + + private final Set attributes; + + public GenericTag(String code) { + this(code, 1); + } + + public GenericTag(String code, Integer nip) { + this(code, nip, new HashSet<>()); + } + + @Override + public void addAttribute(ElementAttribute attribute) { + this.attributes.add(attribute); + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/HideMessageEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/HideMessageEvent.java new file mode 100644 index 000000000..c505044a3 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/HideMessageEvent.java @@ -0,0 +1,28 @@ +package nostr.event.impl; + +import java.util.List; +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.tag.EventTag; + +/** + * @author guilhermegps + * + */ +@Event(name = "Hide Message on Channel", nip = 28) +public class HideMessageEvent extends GenericEvent { + + public HideMessageEvent(@NonNull PublicKey pubKey, @NonNull List tags, String content) { + super(pubKey, Kind.HIDE_MESSAGE, tags, content); + } + + public HideMessageEvent(@NonNull PublicKey pubKey, GenericEvent event, String content) { + super(pubKey, Kind.HIDE_MESSAGE); + this.setContent(content); + this.addTag(EventTag.builder().idEvent(event.getId()).build()); + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java new file mode 100644 index 000000000..d5450f9e4 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java @@ -0,0 +1,59 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import nostr.event.Kind; +import nostr.base.PublicKey; +import java.util.ArrayList; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.extern.java.Log; +import nostr.base.UserProfile; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.util.Nip05Validator; +import nostr.util.NostrException; + +/** + * + * @author squirrel + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Log +@Event(name = "Internet Identifier Metadata Event", nip = 5) +public final class InternetIdentifierMetadataEvent extends GenericEvent { + + public InternetIdentifierMetadataEvent(PublicKey pubKey, List tags, @NonNull UserProfile profile) throws NostrException { + super(pubKey, Kind.SET_METADATA, tags); + this.init(profile); + } + + public InternetIdentifierMetadataEvent(PublicKey pubKey, @NonNull UserProfile profile) throws NostrException { + this(pubKey, new ArrayList(), profile); + } + + private void init(UserProfile profile) throws NostrException { + // NIP-05 validator + Nip05Validator.builder().nip05(profile.getName()).publicKey(getPubKey()).build().validate(); + + setContent(profile); + } + + private void setContent(UserProfile profile) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonString = objectMapper.writeValueAsString(profile); + + // Escape the JSON string + String escapedJsonString = escapeJsonString(jsonString); + + this.setContent(escapedJsonString); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/MentionsEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/MentionsEvent.java new file mode 100644 index 000000000..20f1a212a --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/MentionsEvent.java @@ -0,0 +1,41 @@ +package nostr.event.impl; + +import java.util.List; +import nostr.event.tag.PubKeyTag; +import nostr.event.Kind; +import nostr.base.PublicKey; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.java.Log; +import nostr.base.ITag; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; + +/** + * + * @author squirrel + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Event(name = "Handling Mentions", nip = 8) +@Log +public final class MentionsEvent extends GenericEvent { + + public MentionsEvent(PublicKey pubKey, List tags, String content) { + super(pubKey, Kind.TEXT_NOTE, tags, content); + } + + @SuppressWarnings("unchecked") + @Override + public void update() { + super.update(); + + int index = 0; + + while (getTags().iterator().hasNext()) { + ITag tag = (ITag) getTags().iterator().next(); + String replacement = "#[" + index++ + "]"; + setContent(this.getContent().replace(((PubKeyTag) tag).getPublicKey().toString(), replacement)); + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java new file mode 100644 index 000000000..c38e093dd --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java @@ -0,0 +1,79 @@ +package nostr.event.impl; + +import java.util.logging.Level; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.ArrayList; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.java.Log; +import nostr.base.IEncoder; +import nostr.base.UserProfile; +import nostr.base.PublicKey; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.util.NostrException; + +/** + * + * @author squirrel + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Event(name = "Metadata") +@Log +public final class MetadataEvent extends GenericEvent { + + private static final String NAME_PATTERN = "\\w[\\w\\-]+\\w"; + + @JsonIgnore + private UserProfile profile; + + public MetadataEvent(PublicKey pubKey, UserProfile profile) throws NostrException { + super(pubKey, Kind.SET_METADATA, new ArrayList()); + this.profile = profile; + } + + @Override + protected void validate() { + boolean valid = true; + + var strNameArr = this.profile.getName().split("@"); + if (strNameArr.length == 2) { + var localPart = strNameArr[0]; + valid = localPart.matches(NAME_PATTERN); + } + + if (!valid) { + throw new AssertionError("Invalid profile name: " + this.profile, null); + } + } + + @Override + public void update() { + setContent(); + + super.update(); + } + + private void setContent() { + var mapper = IEncoder.MAPPER; + try { + ObjectNode objNode = JsonNodeFactory.instance.objectNode(); + objNode.set("name", mapper.valueToTree(this.getProfile().getName())); + objNode.set("about", mapper.valueToTree(this.getProfile().getAbout())); + objNode.set("picture", mapper.valueToTree(this.getProfile().getPicture().toString())); + + setContent(mapper.writeValueAsString(objNode)); + } catch (JsonProcessingException | IllegalArgumentException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/MuteUserEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/MuteUserEvent.java new file mode 100644 index 000000000..5392968f7 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/MuteUserEvent.java @@ -0,0 +1,27 @@ +package nostr.event.impl; + +import java.util.List; +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.tag.PubKeyTag; + +/** + * @author guilhermegps + * + */ +@Event(name = "Mute User on Channel", nip = 28) +public class MuteUserEvent extends GenericEvent { + + public MuteUserEvent(@NonNull PublicKey pubKey, @NonNull List tags, String content) { + super(pubKey, Kind.MUTE_USER, tags, content); + } + + public MuteUserEvent(@NonNull PublicKey pubKey, @NonNull PublicKey mutedUser, String content) { + super(pubKey, Kind.MUTE_USER); + this.addTag(PubKeyTag.builder().publicKey(mutedUser).build()); + this.setContent(content); + } +} diff --git a/nostr-event/src/main/java/nostr/event/impl/OtsEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/OtsEvent.java similarity index 76% rename from nostr-event/src/main/java/nostr/event/impl/OtsEvent.java rename to nostr-java-event/src/main/java/nostr/event/impl/OtsEvent.java index 6385c9cdb..ba61ab7ae 100644 --- a/nostr-event/src/main/java/nostr/event/impl/OtsEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/OtsEvent.java @@ -1,11 +1,12 @@ package nostr.event.impl; +import java.util.List; import java.util.Map; import nostr.base.ElementAttribute; import nostr.base.PublicKey; import nostr.base.annotation.Event; -import nostr.event.list.TagList; +import nostr.event.BaseTag; /** * @@ -14,7 +15,7 @@ @Event(name = "OpenTimestamps Attestations for Events", nip = 1) public class OtsEvent extends TextNoteEvent { - public OtsEvent(PublicKey pubKey, TagList tags, String content, String ots) { + public OtsEvent(PublicKey pubKey, List tags, String content, String ots) { super(pubKey, tags, content); var attribute = ElementAttribute.builder().nip(3).value(Map.of("ots", ots)).build(); this.addAttribute(attribute); diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ReactionEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ReactionEvent.java new file mode 100644 index 000000000..fe3380a8d --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ReactionEvent.java @@ -0,0 +1,35 @@ +package nostr.event.impl; + +import java.util.List; +import nostr.event.Kind; +import nostr.base.PublicKey; +import nostr.event.Reaction; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.java.Log; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.tag.EventTag; +import nostr.event.tag.PubKeyTag; + +/** + * + * @author squirrel + */ +@Data +@Log +@EqualsAndHashCode(callSuper = false) +@Event(name = "Reactions", nip = 25) +public class ReactionEvent extends GenericEvent { + + public ReactionEvent(PublicKey pubKey, List tags, Reaction content, GenericEvent sourceEvent) { + super(pubKey, Kind.REACTION, tags, content.getEmoji()); + } + + public ReactionEvent(PublicKey pubKey, GenericEvent event, Reaction content, GenericEvent sourceEvent) { + super(pubKey, Kind.REACTION); + this.setContent(content.getEmoji()); + this.addTag(EventTag.builder().idEvent(event.getId()).build()); + this.addTag(PubKeyTag.builder().publicKey(event.getPubKey()).build()); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ReplaceableEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ReplaceableEvent.java new file mode 100644 index 000000000..2efa9cd63 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ReplaceableEvent.java @@ -0,0 +1,32 @@ +package nostr.event.impl; + +import java.util.List; +import nostr.base.PublicKey; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.java.Log; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; + +/** + * + * @author squirrel + */ +@Data +@Log +@EqualsAndHashCode(callSuper = false) +@Event(name = "Replaceable Events", nip = 16) +public class ReplaceableEvent extends GenericEvent { + + public ReplaceableEvent(PublicKey sender, Integer kind, List tags, String content) { + super(sender, kind, tags, content); + } + + @Override + protected void validate() { + if (this.getKind() >= 10_000 && this.getKind() < 20_000) { + return; + } + throw new AssertionError("Invalid kind value. Must be between 10000 and 20000 (excl)", null); + } +} diff --git a/nostr-event/src/main/java/nostr/event/impl/TextNoteEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/TextNoteEvent.java similarity index 67% rename from nostr-event/src/main/java/nostr/event/impl/TextNoteEvent.java rename to nostr-java-event/src/main/java/nostr/event/impl/TextNoteEvent.java index c8e50f0e0..bc1008758 100644 --- a/nostr-event/src/main/java/nostr/event/impl/TextNoteEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/TextNoteEvent.java @@ -1,10 +1,11 @@ package nostr.event.impl; +import java.util.List; import nostr.base.PublicKey; import nostr.event.Kind; import nostr.base.annotation.Event; -import nostr.event.list.TagList; +import nostr.event.BaseTag; /** * @@ -13,7 +14,7 @@ @Event(name = "Text Note") public class TextNoteEvent extends GenericEvent { - public TextNoteEvent(PublicKey pubKey, TagList tags, String content) { + public TextNoteEvent(PublicKey pubKey, List tags, String content) { super(pubKey, Kind.TEXT_NOTE, tags, content); } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventDecoder.java new file mode 100644 index 000000000..7734e09e3 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventDecoder.java @@ -0,0 +1,33 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.IDecoder; +import nostr.event.BaseEvent; +import nostr.event.impl.GenericEvent; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@Data +@AllArgsConstructor +public class BaseEventDecoder implements IDecoder { + + private final String jsonEvent; + + @Override + public BaseEvent decode() throws NostrException { + try { + var mapper = new ObjectMapper(); + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + return mapper.readValue(jsonEvent, GenericEvent.class); + } catch (JsonProcessingException ex) { + throw new NostrException(ex); + } + } +} diff --git a/nostr-event/src/main/java/nostr/event/marshaller/impl/ElementMarshaller.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java similarity index 51% rename from nostr-event/src/main/java/nostr/event/marshaller/impl/ElementMarshaller.java rename to nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java index b02a7c212..0d8655b22 100644 --- a/nostr-event/src/main/java/nostr/event/marshaller/impl/ElementMarshaller.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java @@ -1,15 +1,17 @@ -package nostr.event.marshaller.impl; +package nostr.event.json.codec; +import com.fasterxml.jackson.core.JsonProcessingException; import java.lang.reflect.Field; -import java.util.logging.Level; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.java.Log; import nostr.base.IElement; -import nostr.base.IMarshaller; +import nostr.base.IEncoder; +import static nostr.base.IEncoder.MAPPER; import nostr.base.NipUtil; import nostr.base.Relay; +import nostr.event.BaseEvent; import nostr.util.NostrException; import nostr.util.UnsupportedNIPException; @@ -20,43 +22,38 @@ @AllArgsConstructor @Log @Data -public class ElementMarshaller implements IMarshaller { + public class BaseEventEncoder implements IEncoder { - private final IElement element; + private final BaseEvent event; private final Relay relay; - @Override - public String marshall() throws UnsupportedNIPException, NostrException { - Relay relay = getRelay(); + public BaseEventEncoder(BaseEvent event) { + this(event, null); + } + @Override + public String encode() throws UnsupportedNIPException, NostrException { if (!nipEventSupport()) { throw new UnsupportedNIPException("NIP is not supported by relay: \"" + relay.getName() + "\" - List of supported NIP(s): " + relay.printSupportedNips()); } - try { - return toJson(); - } catch (NostrException e) { - log.log(Level.SEVERE, null, e); - throw new RuntimeException(e); - } - } - - private boolean nipEventSupport() { - Relay relay = getRelay(); - - return (relay != null) ? NipUtil.checkSupport(relay, getElement()) : true; + return toJson(); } protected boolean nipFieldSupport(Field field) { return (relay != null) ? NipUtil.checkSupport(relay, field) : true; } - - @Override - public String toJson() throws NostrException { - try { - return MAPPER.writeValueAsString(element); - } catch (Exception e) { - throw new NostrException(e); - } + + protected String toJson() throws NostrException { + try { + return MAPPER.writeValueAsString(event); + } catch (JsonProcessingException e) { + throw new NostrException(e); + } } + + private boolean nipEventSupport() { + return (relay != null) ? NipUtil.checkSupport(relay, event) : true; + } + } 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 new file mode 100644 index 000000000..2d71b043b --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java @@ -0,0 +1,124 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.ElementAttribute; +import nostr.base.IDecoder; +import nostr.event.BaseMessage; +import nostr.event.impl.ClientAuthenticationEvent; +import nostr.event.impl.Filters; +import nostr.event.impl.GenericEvent; +import nostr.event.impl.GenericMessage; +import nostr.event.message.BaseAuthMessage; +import nostr.event.message.ClientAuthenticationMessage; +import nostr.event.message.CloseMessage; +import nostr.event.message.EoseMessage; +import nostr.event.message.EventMessage; +import nostr.event.message.NoticeMessage; +import nostr.event.message.OkMessage; +import nostr.event.message.RelayAuthenticationMessage; +import nostr.event.message.ReqMessage; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@Data +@AllArgsConstructor +public class BaseMessageDecoder implements IDecoder { + + private final String jsonString; + + @Override + public BaseMessage decode() throws NostrException { + try { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + var msgArr = mapper.readValue(jsonString, Object[].class); + final String strCmd = msgArr[0].toString(); + final Object arg = msgArr[1]; + BaseMessage message = null; + + if (arg == null) { + throw new AssertionError("arg == null"); + } + + switch (strCmd) { + case "AUTH" -> { + final BaseAuthMessage authMsg; + // Client Auth + if (arg instanceof Map map) { + var event = mapper.convertValue(map, new TypeReference() { + }); + authMsg = new ClientAuthenticationMessage(event); + } else { + // Relay Auth + final var challenge = arg.toString(); + authMsg = new RelayAuthenticationMessage(challenge); + } + message = authMsg; + } + case "CLOSE" -> + message = new CloseMessage(arg.toString()); + case "EOSE" -> + message = new EoseMessage(arg.toString()); + case "EVENT" -> { + if (msgArr.length == 2 && arg instanceof Map map) { + var event = mapper.convertValue(map, new TypeReference() { + }); + message = new EventMessage(event); + } else if (msgArr.length == 3 && arg instanceof String) { + var subId = arg.toString(); + if (msgArr[2] instanceof Map map) { + var event = mapper.convertValue(map, new TypeReference() { + }); + message = new EventMessage(event, subId); + } + } else { + throw new AssertionError("Invalid argument: " + arg); + } + } + case "NOTICE" -> + message = new NoticeMessage(arg.toString()); + case "OK" -> { + if (msgArr.length == 4 && msgArr[2] instanceof Boolean duplicate) { + String msgArg = msgArr[3].toString(); + message = new OkMessage(arg.toString(), duplicate, msgArg); + } else { + throw new AssertionError("Invalid argument: " + msgArr[2]); + } + } + // TODO - Cater for more than one filters. Create issue in Github + case "REQ" -> { + if (arg instanceof Map map) { + var filters = mapper.convertValue(map, new TypeReference() { + }); + message = new ReqMessage(arg.toString(), filters); + } else { + throw new AssertionError("Invalid argument: " + msgArr[2]); + } + } + default -> { + //throw new AssertionError("Invalid command " + strCmd); + // NOTE: Only String attribute suppoeted. It would be impossible to guess the object's type + GenericMessage gm = new GenericMessage(strCmd); + for (int i = 1; i < msgArr.length; i++) { + if (msgArr[i] instanceof String) { + gm.addAttribute(ElementAttribute.builder().value(msgArr[i]).build()); + } + } + } + } + return message; + } catch (JsonProcessingException ex) { + throw new NostrException(ex); + } + } + +} diff --git a/nostr-event/src/main/java/nostr/event/marshaller/impl/MessageMarshaller.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageEncoder.java similarity index 54% rename from nostr-event/src/main/java/nostr/event/marshaller/impl/MessageMarshaller.java rename to nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageEncoder.java index ef0167189..7bab4a1ff 100644 --- a/nostr-event/src/main/java/nostr/event/marshaller/impl/MessageMarshaller.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageEncoder.java @@ -1,15 +1,22 @@ -package nostr.event.marshaller.impl; +package nostr.event.json.codec; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; - +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.IEncoder; +import static nostr.base.IEncoder.MAPPER; import nostr.base.Relay; +import nostr.event.BaseEvent; +import nostr.event.BaseMessage; import nostr.event.impl.GenericMessage; -import nostr.event.message.AuthMessage; +import nostr.event.message.ClientAuthenticationMessage; import nostr.event.message.CloseMessage; import nostr.event.message.EventMessage; import nostr.event.message.NoticeMessage; +import nostr.event.message.RelayAuthenticationMessage; import nostr.event.message.ReqMessage; import nostr.util.NostrException; @@ -17,32 +24,36 @@ * * @author squirrel */ -public class MessageMarshaller extends ElementMarshaller { - - public MessageMarshaller(GenericMessage baseMessage, Relay relay) { - super(baseMessage, relay); - } +@Data +@AllArgsConstructor +public class BaseMessageEncoder implements IEncoder { + private final BaseMessage message; + private final Relay relay; + @Override - public String marshall() throws NostrException { - GenericMessage message = (GenericMessage) getElement(); - Relay relay = getRelay(); + public String encode() throws NostrException { var arrayNode = JsonNodeFactory.instance.arrayNode(); try { arrayNode.add(message.getCommand()); if (message instanceof EventMessage msg) { - JsonNode tree = MAPPER.readTree(new ElementMarshaller(msg.getEvent(), relay).marshall()); + JsonNode tree = MAPPER.readTree(new BaseEventEncoder((BaseEvent) msg.getEvent(), relay).encode()); arrayNode.add(tree); } else if (message instanceof ReqMessage msg) { arrayNode.add(msg.getSubscriptionId()); - JsonNode tree = MAPPER.readTree(new ElementMarshaller(msg.getFilters(), relay).marshall()); + JsonNode tree = MAPPER.readTree(new BaseEventEncoder(msg.getFilters(), relay).encode()); arrayNode.add(tree); } else if (message instanceof NoticeMessage msg) { arrayNode.add(msg.getMessage()); } else if (message instanceof CloseMessage msg) { arrayNode.add(msg.getSubscriptionId()); - } else if (message instanceof AuthMessage msg) { + } else if (message instanceof ClientAuthenticationMessage msg) { arrayNode.add(msg.getEvent().toString()); + } else if (message instanceof RelayAuthenticationMessage msg){ + arrayNode.add(msg.getChallenge()); + } else if (message instanceof GenericMessage msg) { + arrayNode.add(msg.getCommand()); + msg.getAttributes().stream().map(a -> a.getValue()).forEach(v -> arrayNode.add(v.toString())); } else { throw new NostrException(String.format("Invalid message type %s", message)); } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java new file mode 100644 index 000000000..ff60f76d9 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java @@ -0,0 +1,31 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.IDecoder; +import nostr.event.BaseTag; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@Data +@AllArgsConstructor +public class BaseTagDecoder implements IDecoder { + + private final String jsonString; + + @Override + public BaseTag decode() throws NostrException { + try { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonString, BaseTag.class); + } catch (JsonProcessingException ex) { + throw new NostrException(ex); + } + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java new file mode 100644 index 000000000..d517ac49d --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java @@ -0,0 +1,46 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import lombok.AllArgsConstructor; + +import lombok.Data; +import lombok.NonNull; +import nostr.base.IEncoder; +import nostr.base.Relay; +import nostr.event.BaseTag; +import nostr.event.json.serializer.TagSerializer; +import nostr.util.NostrException; + +/** + * @author guilhermegps + * + */ +@Data +@AllArgsConstructor +public class BaseTagEncoder implements IEncoder { + + private final BaseTag tag; + private final Relay relay; + + public BaseTagEncoder(@NonNull BaseTag tag) { + this(tag, null); + } + + @Override + public String encode() throws NostrException { + try { + SimpleModule module = new SimpleModule(); + module.addSerializer(new TagSerializer()); + var mapper = (new ObjectMapper()) + .setSerializationInclusion(Include.NON_NULL) + .registerModule(module); + + return mapper.writeValueAsString(tag); + } catch (JsonProcessingException e) { + throw new NostrException(e); + } + } +} 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 new file mode 100644 index 000000000..1ad09859f --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java @@ -0,0 +1,31 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.IDecoder; +import nostr.event.impl.Filters; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@Data +@AllArgsConstructor +public class FiltersDecoder implements IDecoder { + + private final String jsonString; + + @Override + public Filters decode() throws NostrException { + try { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonString, Filters.class); + } catch (JsonProcessingException ex) { + throw new NostrException(ex); + } + } + +} diff --git a/nostr-event/src/main/java/nostr/event/marshaller/impl/FilterMarshaller.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java similarity index 67% rename from nostr-event/src/main/java/nostr/event/marshaller/impl/FilterMarshaller.java rename to nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index 753682d2f..0ef02a0ff 100644 --- a/nostr-event/src/main/java/nostr/event/marshaller/impl/FilterMarshaller.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -1,5 +1,6 @@ -package nostr.event.marshaller.impl; +package nostr.event.json.codec; +import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.StreamSupport; @@ -8,10 +9,8 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; -import nostr.base.IMarshaller; +import lombok.EqualsAndHashCode; import nostr.base.Relay; import nostr.event.impl.Filters; import nostr.util.NostrException; @@ -20,23 +19,22 @@ * @author guilhermegps * */ -@AllArgsConstructor @Data -@Builder -public class FilterMarshaller implements IMarshaller { +@EqualsAndHashCode(callSuper = false) +public class FiltersEncoder extends BaseEventEncoder { - private final Filters filters; - private final Relay relay; + public FiltersEncoder(Filters filters, Relay relay) { + super(filters, relay); + } - @Override - public String marshall() throws NostrException { - return toJson(); + public FiltersEncoder(Filters filters) { + super(filters); } @Override - public String toJson() throws NostrException { + protected String toJson() throws NostrException { try { - JsonNode node = MAPPER.valueToTree(filters); + JsonNode node = MAPPER.valueToTree((Filters) getEvent()); ObjectNode objNode = (ObjectNode) node; var arrayNode = (ArrayNode) node.get("genericTagQueryList"); if (arrayNode != null && !arrayNode.isNull()) { @@ -51,7 +49,7 @@ public String toJson() throws NostrException { objNode.remove("genericTagQueryList"); return MAPPER.writeValueAsString(node); - } catch (Exception e) { + } catch (JsonProcessingException | IllegalArgumentException e) { throw new NostrException(e); } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java new file mode 100644 index 000000000..f0c636a64 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java @@ -0,0 +1,39 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashSet; +import java.util.Set; +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.ElementAttribute; +import nostr.base.IDecoder; +import nostr.event.impl.GenericTag; +import nostr.util.NostrException; + +@Data +@AllArgsConstructor +public class GenericTagDecoder implements IDecoder { + + private final String json; + + @Override + public GenericTag decode() throws NostrException { + try { + ObjectMapper objectMapper = new ObjectMapper(); + String[] jsonElements = objectMapper.readValue(this.json, String[].class); + + String code = jsonElements[0]; + + Set attributes = new HashSet<>(); + for (int i = 1; i < jsonElements.length; i++) { + ElementAttribute attribute = new ElementAttribute(null, jsonElements[i], null); + attributes.add(attribute); + } + + return new GenericTag(code, null, attributes); + } catch (JsonProcessingException ex) { + throw new NostrException(ex); + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagEncoder.java new file mode 100644 index 000000000..fe35ef684 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagEncoder.java @@ -0,0 +1,31 @@ +package nostr.event.json.codec; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NonNull; +import nostr.base.IEncoder; +import nostr.base.Relay; +import nostr.event.impl.GenericTag; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@Data +@AllArgsConstructor +public class GenericTagEncoder implements IEncoder { + + private final GenericTag tag; + private final Relay relay; + + public GenericTagEncoder(@NonNull GenericTag tag) { + this(tag, null); + } + + @Override + public String encode() throws NostrException { + var encoder = new BaseTagEncoder(tag, relay); + return encoder.encode(); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryDecoder.java new file mode 100644 index 000000000..1748430d0 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryDecoder.java @@ -0,0 +1,24 @@ +package nostr.event.json.codec; + +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.GenericTagQuery; +import nostr.base.IDecoder; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@AllArgsConstructor +@Data +public class GenericTagQueryDecoder implements IDecoder { + + private final String json; + + @Override + public GenericTagQuery decode() throws NostrException { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryEncoder.java new file mode 100644 index 000000000..32522e989 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryEncoder.java @@ -0,0 +1,38 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.AllArgsConstructor; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import nostr.base.IEncoder; +import static nostr.base.IEncoder.MAPPER; +import nostr.base.Relay; +import nostr.base.GenericTagQuery; +import nostr.util.NostrException; + +/** + * @author guilhermegps + * + */ +@Data +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +public class GenericTagQueryEncoder implements IEncoder { + + private final GenericTagQuery genericTagQuery; + private final Relay relay; + + public GenericTagQueryEncoder(GenericTagQuery tag) { + this(tag, null); + } + + @Override + public String encode() throws NostrException { + try { + return MAPPER.writeValueAsString(genericTagQuery); + } catch (JsonProcessingException e) { + throw new NostrException(e); + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java new file mode 100644 index 000000000..8ce941b6f --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java @@ -0,0 +1,29 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; +import lombok.Data; +import nostr.base.IDecoder; +import nostr.event.Nip05Content; +import nostr.util.NostrException; + +/** + * + * @author eric + */ +@Data +@AllArgsConstructor +public class Nip05ContentDecoder implements IDecoder { + + private final String jsonContent; + + @Override + public Nip05Content decode() throws NostrException { + try { + return new ObjectMapper().readValue(this.jsonContent, Nip05Content.class); + } catch (JsonProcessingException ex) { + throw new NostrException(ex); + } + } +} diff --git a/nostr-event/src/main/java/nostr/event/marshaller/impl/OstEventMarshaller.java b/nostr-java-event/src/main/java/nostr/event/json/codec/OstEventEncoder.java similarity index 77% rename from nostr-event/src/main/java/nostr/event/marshaller/impl/OstEventMarshaller.java rename to nostr-java-event/src/main/java/nostr/event/json/codec/OstEventEncoder.java index 78aed934b..d21cce57d 100644 --- a/nostr-event/src/main/java/nostr/event/marshaller/impl/OstEventMarshaller.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/OstEventEncoder.java @@ -1,15 +1,15 @@ -package nostr.event.marshaller.impl; +package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.HashMap; - import lombok.AllArgsConstructor; -import lombok.Builder; + import lombok.Data; import nostr.base.ElementAttribute; -import nostr.base.IMarshaller; +import nostr.base.IEncoder; +import static nostr.base.IEncoder.MAPPER; import nostr.base.Relay; import nostr.event.impl.OtsEvent; import nostr.util.NostrException; @@ -18,21 +18,23 @@ * @author guilhermegps * */ -@AllArgsConstructor @Data -@Builder -public class OstEventMarshaller implements IMarshaller { +@AllArgsConstructor +public class OstEventEncoder implements IEncoder { private final OtsEvent event; private final Relay relay; + + public OstEventEncoder(OtsEvent event) { + this(event, null); + } @Override - public String marshall() throws NostrException { + public String encode() throws NostrException { return toJson(); } - @Override - public String toJson() throws NostrException { + private String toJson() throws NostrException { try { JsonNode node = MAPPER.valueToTree(event); ObjectNode objNode = (ObjectNode) node; diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomGenericTagQueryDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomGenericTagQueryDeserializer.java new file mode 100644 index 000000000..f69c365ca --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomGenericTagQueryDeserializer.java @@ -0,0 +1,37 @@ +package nostr.event.json.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import nostr.base.GenericTagQuery; + +public class CustomGenericTagQueryDeserializer extends JsonDeserializer { + + @Override + public GenericTagQuery deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + + var genericTagQuery = new GenericTagQuery(); + + Iterator> fields = rootNode.fields(); + if (fields.hasNext()) { + Map.Entry field = fields.next(); + String tagName = field.getKey(); + JsonNode valuesNode = field.getValue(); + List values = objectMapper.convertValue(valuesNode, ArrayList.class); + genericTagQuery.setTagName(tagName.charAt(1)); // Assuming tagName is always a single character preceded by '#' + genericTagQuery.setValue(values); + } + + return genericTagQuery; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomGenericTagQueryListDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomGenericTagQueryListDeserializer.java new file mode 100644 index 000000000..e6b434bb9 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomGenericTagQueryListDeserializer.java @@ -0,0 +1,21 @@ +package nostr.event.json.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import java.io.IOException; +import nostr.event.list.GenericTagQueryList; + +/** + * + * @author eric + */ +public class CustomGenericTagQueryListDeserializer extends JsonDeserializer { + + @Override + public GenericTagQueryList deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/PublicKeyDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/PublicKeyDeserializer.java new file mode 100644 index 000000000..cf12c1a7b --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/deserializer/PublicKeyDeserializer.java @@ -0,0 +1,20 @@ + +package nostr.event.json.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import nostr.base.PublicKey; + +public class PublicKeyDeserializer extends JsonDeserializer { + + @Override + public PublicKey deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + JsonNode node = jsonParser.readValueAsTree(); + String hexPubKey = node.asText(); + + return new PublicKey(hexPubKey); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/SignatureDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/SignatureDeserializer.java new file mode 100644 index 000000000..f6917ba2e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/deserializer/SignatureDeserializer.java @@ -0,0 +1,28 @@ +package nostr.event.json.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import nostr.base.Signature; +import nostr.util.NostrUtil; + +public class SignatureDeserializer extends JsonDeserializer { + + @Override + public Signature deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + ObjectMapper objectMapper = (ObjectMapper) jsonParser.getCodec(); + JsonNode node = objectMapper.readTree(jsonParser); + + String sigValue = node.asText(); + byte[] rawData = NostrUtil.hexToBytes(sigValue); + + Signature signature = new Signature(); + signature.setRawData(rawData); + + return signature; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java new file mode 100644 index 000000000..359fd6fae --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java @@ -0,0 +1,111 @@ +package nostr.event.json.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; +import nostr.base.PublicKey; +import nostr.event.BaseTag; +import nostr.event.Marker; +import nostr.event.json.codec.GenericTagDecoder; +import nostr.event.tag.EventTag; +import nostr.event.tag.NonceTag; +import nostr.event.tag.PubKeyTag; +import nostr.event.tag.SubjectTag; +import nostr.util.NostrException; + +public class TagDeserializer extends JsonDeserializer { + + @Override + public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + // Extract relevant data from the JSON node + String code = node.get(0).asText(); + + if (null == code) { + throw new IOException("Unknown tag code: " + code); + } else // Perform custom deserialization logic based on the concrete class + { + switch (code) { + case "p" -> { + // Deserialization logic for ConcreteTag1 + PubKeyTag tag = new PubKeyTag(); + + final JsonNode nodePubKey = node.get(1); + if (nodePubKey != null) { + tag.setPublicKey(new PublicKey(nodePubKey.asText())); + } + + final JsonNode nodeMainUrl = node.get(2); + if (nodeMainUrl != null) { + tag.setMainRelayUrl(nodeMainUrl.asText()); + } + + final JsonNode nodePetName = node.get(3); + if (nodePetName != null) { + tag.setPetName(nodePetName.asText()); + } + + return (T) tag; + } + + case "nonce" -> { + // Deserialization logic for ConcreteTag2 + NonceTag tag = new NonceTag(); + + final JsonNode nodeNonce = node.get(1); + if (nodeNonce != null) { + tag.setNonce(Integer.valueOf(nodeNonce.asText())); + } + + final JsonNode nodeDifficulty = node.get(1); + if (nodeDifficulty != null) { + tag.setDifficulty(Integer.valueOf(nodeDifficulty.asText())); + } + return (T) tag; + } + case "e" -> { + // Deserialization logic for ConcreteTag2 + EventTag tag = new EventTag(); + + final JsonNode nodeIdEvent = node.get(1); + if (nodeIdEvent != null) { + tag.setIdEvent(nodeIdEvent.asText()); + } + + final JsonNode nodeRelay = node.get(2); + if (nodeRelay != null) { + tag.setRecommendedRelayUrl(nodeRelay.asText()); + } + + final JsonNode nodeMarker = node.get(3); + if (nodeMarker != null) { + tag.setMarker(Marker.valueOf(nodeMarker.asText().toUpperCase())); + } + return (T) tag; + } + case "subject" -> { + SubjectTag tag = new SubjectTag(); + + final JsonNode nodeSubject = node.get(1); + if (nodeSubject != null) { + tag.setSubject(nodeSubject.asText()); + } + return (T) tag; + } + default -> { + try { + var tag = new GenericTagDecoder(node.toString()).decode(); + return (T) tag; + } catch (NostrException ex) { + throw new IOException(ex); + } + } + + } + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomBaseListSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomBaseListSerializer.java new file mode 100644 index 000000000..f7dea8f7c --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomBaseListSerializer.java @@ -0,0 +1,64 @@ +package nostr.event.json.serializer; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import lombok.extern.java.Log; +import nostr.base.IEncoder; +import nostr.event.list.BaseList; + +/** + * @author guilhermegps + * + */ +@Log +public class CustomBaseListSerializer extends JsonSerializer { + + @Override + public void serialize(BaseList value, JsonGenerator gen, SerializerProvider serializers) { + try { + var list = value.getList().parallelStream().map(obj -> toJson(obj)) + .collect(Collectors.toList()); + + gen.writePOJO(list); + } catch (IOException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + + private JsonNode toJson(Object obj) { + var mapper = IEncoder.MAPPER; + try { + JsonNode node = mapper.valueToTree(obj); + + if (node.isObject()) { + Iterator> fields = node.fields(); + + var list = StreamSupport.stream( + Spliterators.spliteratorUnknownSize(fields, Spliterator.ORDERED), false) + .map(f -> f.getValue().asText().toLowerCase()) + .collect(Collectors.toList()); + + return mapper.valueToTree(list); + } + + return node; + } catch (IllegalArgumentException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomGenericTagQueryListSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomGenericTagQueryListSerializer.java new file mode 100644 index 000000000..6716a9256 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomGenericTagQueryListSerializer.java @@ -0,0 +1,54 @@ +package nostr.event.json.serializer; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import lombok.extern.java.Log; +import nostr.base.GenericTagQuery; +import nostr.base.IEncoder; +import nostr.event.list.GenericTagQueryList; + +/** + * @author guilhermegps + * + */ +@Log +public class CustomGenericTagQueryListSerializer extends JsonSerializer { + + @Override + public void serialize(GenericTagQueryList value, JsonGenerator gen, SerializerProvider serializers) { + try { + var list = value.getList().parallelStream().map(gtq -> toJson(gtq)) + .collect(Collectors.toList()); + + gen.writePOJO(list); + } catch (IOException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + + private JsonNode toJson(GenericTagQuery gtq) { + var mapper = IEncoder.MAPPER; + try { + JsonNode node = mapper.valueToTree(gtq); + ObjectNode objNode = (ObjectNode) node; + objNode.set("#" + node.get("tagName").textValue(), node.get("value")); + objNode.remove("tagName"); + objNode.remove("value"); + + return node; + } catch (IllegalArgumentException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomGenericTagQuerySerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomGenericTagQuerySerializer.java new file mode 100644 index 000000000..3a5f6966e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomGenericTagQuerySerializer.java @@ -0,0 +1,55 @@ +package nostr.event.json.serializer; + +import java.io.IOException; +import java.util.logging.Level; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import lombok.extern.java.Log; +import nostr.base.GenericTagQuery; +import nostr.base.IEncoder; + +/** + * @author guilhermegps + * + */ +@Log +public class CustomGenericTagQuerySerializer extends StdSerializer { + + private static final long serialVersionUID = 6803478463890319884L; + + public CustomGenericTagQuerySerializer() { + super(GenericTagQuery.class); + } + + @Override + public void serialize(GenericTagQuery value, JsonGenerator gen, SerializerProvider serializers) { + try { + gen.writePOJO(toJson(value)); + } catch (IOException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + + private static JsonNode toJson(GenericTagQuery gtq) { + var mapper = IEncoder.MAPPER; + try { + JsonNode node = mapper.valueToTree(gtq); + ObjectNode objNode = (ObjectNode) node; + objNode.set("#" + node.get("tagName").textValue(), node.get("value")); + objNode.remove("tagName"); + objNode.remove("value"); + + return node; + } catch (IllegalArgumentException e) { + log.log(Level.SEVERE, null, e); + throw new RuntimeException(e); + } + } + +} diff --git a/nostr-event/src/main/java/nostr/event/serializer/CustomIdEventListSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomIdEventListSerializer.java similarity index 61% rename from nostr-event/src/main/java/nostr/event/serializer/CustomIdEventListSerializer.java rename to nostr-java-event/src/main/java/nostr/event/json/serializer/CustomIdEventListSerializer.java index 8a1f8bbac..f95e72162 100644 --- a/nostr-event/src/main/java/nostr/event/serializer/CustomIdEventListSerializer.java +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomIdEventListSerializer.java @@ -1,4 +1,4 @@ -package nostr.event.serializer; +package nostr.event.json.serializer; import java.io.IOException; import java.util.logging.Level; @@ -12,7 +12,6 @@ import nostr.event.impl.GenericEvent; import nostr.event.list.EventList; - /** * @author guilhermegps * @@ -20,16 +19,16 @@ @Log public class CustomIdEventListSerializer extends JsonSerializer { - @Override - public void serialize(EventList value, JsonGenerator gen, SerializerProvider serializers) { - try { - var list = value.getList().parallelStream().map(GenericEvent::getId).collect(Collectors.toList()); - - gen.writePOJO(list); - } catch (IOException e) { + @Override + public void serialize(EventList value, JsonGenerator gen, SerializerProvider serializers) { + try { + var list = value.getList().parallelStream().map(GenericEvent::getId).collect(Collectors.toList()); + + gen.writePOJO(list); + } catch (IOException e) { log.log(Level.SEVERE, null, e); throw new RuntimeException(e); - } - } + } + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java new file mode 100644 index 000000000..008f1d30e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java @@ -0,0 +1,74 @@ +package nostr.event.json.serializer; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Set; + +import lombok.extern.java.Log; +import nostr.base.ElementAttribute; +import nostr.event.BaseTag; +import nostr.event.impl.GenericTag; +import nostr.util.NostrException; + +/** + * @author guilhermegps + * + */ +@Log +public class TagSerializer extends StdSerializer { + + private static final long serialVersionUID = -3877972991082754068L; + + public TagSerializer() { + super(BaseTag.class); + } + + @Override + public void serialize(BaseTag value, JsonGenerator gen, SerializerProvider serializers) { + try { + // -- Create the node + final ObjectNode node = new ObjectNode(JsonNodeFactory.instance); + List fields = value.getSupportedFields(null); + + // Populate the node with the fields data + fields.stream().forEach((Field f) -> { + try { + node.put(f.getName(), value.getFieldValue(f)); + } catch (NostrException ex) { + throw new RuntimeException(ex); + } + }); + + // Populate the node with the attributes data + if (value instanceof GenericTag genericTag) { + Set attrs = genericTag.getAttributes(); + attrs.stream().forEach(a -> node.put(a.getName(), a.getValue().toString())); + } + + // Extract the property values from the node and serialize them as an array + if (node.isObject()) { + ArrayNode arrayNode = node.objectNode().putArray("values"); + + // Add the tag code as the first element + arrayNode.add(value.getCode()); + node.fields().forEachRemaining(entry -> arrayNode.add(entry.getValue().asText())); + + gen.writePOJO(arrayNode); + } else { + throw new AssertionError("node.isObject()", new RuntimeException()); + } + + } catch (IOException | NostrException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/nostr-event/src/main/java/nostr/event/list/BaseList.java b/nostr-java-event/src/main/java/nostr/event/list/BaseList.java similarity index 69% rename from nostr-event/src/main/java/nostr/event/list/BaseList.java rename to nostr-java-event/src/main/java/nostr/event/list/BaseList.java index f59b7bf08..f09ae47fd 100644 --- a/nostr-event/src/main/java/nostr/event/list/BaseList.java +++ b/nostr-java-event/src/main/java/nostr/event/list/BaseList.java @@ -1,4 +1,3 @@ - package nostr.event.list; import java.util.List; @@ -10,7 +9,7 @@ import lombok.NonNull; import nostr.base.INostrList; import nostr.base.annotation.JsonList; -import nostr.event.serializer.CustomBaseListSerializer; +import nostr.event.json.serializer.CustomBaseListSerializer; /** * @@ -20,7 +19,7 @@ @AllArgsConstructor @Data @JsonList -@JsonSerialize(using=CustomBaseListSerializer.class) +@JsonSerialize(using = CustomBaseListSerializer.class) public abstract class BaseList implements INostrList { @NonNull @@ -38,8 +37,18 @@ public void addAll(@NonNull INostrList aList) { this.list.addAll(aList.getList()); } + public void addAll(@NonNull List aList) { + aList.stream().forEach(e -> this.list.add(e)); + } + @Override public int size() { return this.list.size(); } + + @Override + public Integer getNip() { + return 1; + } + } diff --git a/nostr-event/src/main/java/nostr/event/list/EventList.java b/nostr-java-event/src/main/java/nostr/event/list/EventList.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/list/EventList.java rename to nostr-java-event/src/main/java/nostr/event/list/EventList.java diff --git a/nostr-event/src/main/java/nostr/event/list/FiltersList.java b/nostr-java-event/src/main/java/nostr/event/list/FiltersList.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/list/FiltersList.java rename to nostr-java-event/src/main/java/nostr/event/list/FiltersList.java diff --git a/nostr-event/src/main/java/nostr/event/list/GenericTagQueryList.java b/nostr-java-event/src/main/java/nostr/event/list/GenericTagQueryList.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/list/GenericTagQueryList.java rename to nostr-java-event/src/main/java/nostr/event/list/GenericTagQueryList.java diff --git a/nostr-event/src/main/java/nostr/event/list/KindList.java b/nostr-java-event/src/main/java/nostr/event/list/KindList.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/list/KindList.java rename to nostr-java-event/src/main/java/nostr/event/list/KindList.java diff --git a/nostr-event/src/main/java/nostr/event/list/PublicKeyList.java b/nostr-java-event/src/main/java/nostr/event/list/PublicKeyList.java similarity index 100% rename from nostr-event/src/main/java/nostr/event/list/PublicKeyList.java rename to nostr-java-event/src/main/java/nostr/event/list/PublicKeyList.java diff --git a/nostr-java-event/src/main/java/nostr/event/message/BaseAuthMessage.java b/nostr-java-event/src/main/java/nostr/event/message/BaseAuthMessage.java new file mode 100644 index 000000000..e2d4f5f3c --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/message/BaseAuthMessage.java @@ -0,0 +1,19 @@ +package nostr.event.message; + +import nostr.event.BaseMessage; + +/** + * + * @author eric + */ +public abstract class BaseAuthMessage extends BaseMessage { + + public BaseAuthMessage(String command) { + super(command); + } + + @Override + public Integer getNip() { + return 42; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/message/ClientAuthenticationMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ClientAuthenticationMessage.java new file mode 100644 index 000000000..d8cf3be3c --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/message/ClientAuthenticationMessage.java @@ -0,0 +1,28 @@ + +package nostr.event.message; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import nostr.base.Command; +import nostr.event.impl.ClientAuthenticationEvent; + +/** + * + * @author eric + */ +@Data +@EqualsAndHashCode(callSuper = false) +@ToString(callSuper = true) +public class ClientAuthenticationMessage extends BaseAuthMessage { + + @JsonProperty + private final ClientAuthenticationEvent event; + + public ClientAuthenticationMessage(ClientAuthenticationEvent event) { + super(Command.AUTH.name()); + this.event = event; + } + +} \ No newline at end of file diff --git a/nostr-event/src/main/java/nostr/event/message/CloseMessage.java b/nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java similarity index 79% rename from nostr-event/src/main/java/nostr/event/message/CloseMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java index b4f5682a0..07e5ef638 100644 --- a/nostr-event/src/main/java/nostr/event/message/CloseMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java @@ -5,11 +5,12 @@ */ package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import nostr.base.Command; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; /** * @@ -18,8 +19,9 @@ @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) -public class CloseMessage extends GenericMessage { +public class CloseMessage extends BaseMessage { + @JsonProperty private final String subscriptionId; public CloseMessage(String subscriptionId) { diff --git a/nostr-event/src/main/java/nostr/event/message/ContactListMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ContactListMessage.java similarity index 69% rename from nostr-event/src/main/java/nostr/event/message/ContactListMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/ContactListMessage.java index 027ca15e0..9b5b70621 100644 --- a/nostr-event/src/main/java/nostr/event/message/ContactListMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ContactListMessage.java @@ -1,17 +1,20 @@ package nostr.event.message; +import java.util.List; +import lombok.ToString; import nostr.event.Kind; import nostr.base.PublicKey; +import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; -import nostr.event.list.PubKeyTagList; /** * * @author squirrel */ +@ToString public class ContactListMessage extends EventMessage { - public ContactListMessage(PubKeyTagList contactList, PublicKey publicKey) { + public ContactListMessage(List contactList, PublicKey publicKey) { super(new GenericEvent(publicKey, Kind.CONTACT_LIST, contactList)); } diff --git a/nostr-event/src/main/java/nostr/event/message/EoseMessage.java b/nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java similarity index 72% rename from nostr-event/src/main/java/nostr/event/message/EoseMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java index 37ea26652..97f5efbbd 100644 --- a/nostr-event/src/main/java/nostr/event/message/EoseMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java @@ -1,11 +1,12 @@ package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import nostr.base.Command; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; /** * @@ -14,8 +15,9 @@ @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) -public class EoseMessage extends GenericMessage { +public class EoseMessage extends BaseMessage { + @JsonProperty private final String subscriptionId; public EoseMessage(String subId) { diff --git a/nostr-event/src/main/java/nostr/event/message/EventMessage.java b/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java similarity index 55% rename from nostr-event/src/main/java/nostr/event/message/EventMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/EventMessage.java index 1e3d64d27..0ef85c540 100644 --- a/nostr-event/src/main/java/nostr/event/message/EventMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java @@ -1,13 +1,14 @@ package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; import lombok.ToString; import nostr.base.Command; import nostr.base.IEvent; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; /** * @@ -16,12 +17,21 @@ @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) -public class EventMessage extends GenericMessage { +public class EventMessage extends BaseMessage { + @JsonProperty private final IEvent event; + + @JsonProperty + private String subscriptionId; public EventMessage(@NonNull IEvent event) { + this(event, null); + } + + public EventMessage(@NonNull IEvent event, String subscriptionId) { super(Command.EVENT.name()); this.event = event; + this.subscriptionId = subscriptionId; } } diff --git a/nostr-event/src/main/java/nostr/event/message/NoticeMessage.java b/nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java similarity index 72% rename from nostr-event/src/main/java/nostr/event/message/NoticeMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java index 7af544db0..1b1334113 100644 --- a/nostr-event/src/main/java/nostr/event/message/NoticeMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java @@ -1,11 +1,12 @@ package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import nostr.base.Command; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; /** * @@ -14,8 +15,9 @@ @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) -public class NoticeMessage extends GenericMessage { +public class NoticeMessage extends BaseMessage { + @JsonProperty private final String message; public NoticeMessage(String message) { diff --git a/nostr-event/src/main/java/nostr/event/message/OkMessage.java b/nostr-java-event/src/main/java/nostr/event/message/OkMessage.java similarity index 55% rename from nostr-event/src/main/java/nostr/event/message/OkMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/OkMessage.java index df1753db4..c9770a3cd 100644 --- a/nostr-event/src/main/java/nostr/event/message/OkMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/OkMessage.java @@ -1,17 +1,29 @@ package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import nostr.base.Command; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; /** * * @author squirrel */ -public class OkMessage extends GenericMessage { +@Data +@EqualsAndHashCode(callSuper = false) +@ToString +public class OkMessage extends BaseMessage { + @JsonProperty private final String eventId; + + @JsonProperty private final Boolean flag; + + @JsonProperty private final String message; public OkMessage(String eventId, Boolean flag, String message) { diff --git a/nostr-event/src/main/java/nostr/event/message/AuthMessage.java b/nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java similarity index 50% rename from nostr-event/src/main/java/nostr/event/message/AuthMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java index 548e69714..8ffb0fca9 100644 --- a/nostr-event/src/main/java/nostr/event/message/AuthMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java @@ -1,12 +1,10 @@ - package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import nostr.base.Command; -import nostr.base.IEvent; -import nostr.event.impl.GenericMessage; /** * @@ -15,13 +13,14 @@ @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) -public class AuthMessage extends GenericMessage { +public class RelayAuthenticationMessage extends BaseAuthMessage { + + @JsonProperty + private final String challenge; - private final IEvent event; - - public AuthMessage(IEvent event) { + public RelayAuthenticationMessage(String challenge) { super(Command.AUTH.name()); - this.event = event; + this.challenge = challenge; } - -} \ No newline at end of file + +} diff --git a/nostr-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java similarity index 75% rename from nostr-event/src/main/java/nostr/event/message/ReqMessage.java rename to nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index 148cc9cb6..8684cdc67 100644 --- a/nostr-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -1,12 +1,13 @@ package nostr.event.message; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import nostr.base.Command; +import nostr.event.BaseMessage; import nostr.event.impl.Filters; -import nostr.event.impl.GenericMessage; /** * @@ -15,9 +16,12 @@ @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) -public class ReqMessage extends GenericMessage { +public class ReqMessage extends BaseMessage { + @JsonProperty private final String subscriptionId; + + @JsonProperty private final Filters filters; public ReqMessage(String subscriptionId, Filters filters) { diff --git a/nostr-event/src/main/java/nostr/event/tag/DelegationTag.java b/nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java similarity index 61% rename from nostr-event/src/main/java/nostr/event/tag/DelegationTag.java rename to nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java index a97a37772..11657b879 100644 --- a/nostr-event/src/main/java/nostr/event/tag/DelegationTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java @@ -1,13 +1,17 @@ package nostr.event.tag; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nostr.base.ISignable; import nostr.base.annotation.Key; import nostr.event.BaseTag; import nostr.base.PublicKey; import nostr.base.Signature; import java.beans.Transient; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.extern.java.Log; import nostr.base.annotation.Tag; @@ -19,26 +23,32 @@ @Log @EqualsAndHashCode(callSuper = false) @Tag(code = "delegation", nip = 26) +@AllArgsConstructor +@NoArgsConstructor +@JsonPropertyOrder({"pubkey", "conditions", "signature"}) public class DelegationTag extends BaseTag implements ISignable { @Key - private PublicKey delegatee; + @JsonProperty("delegator") + private PublicKey delegator; @Key + @JsonProperty("conditions") private String conditions; @Key + @JsonProperty("token") private Signature signature; - public DelegationTag(PublicKey delegatee, String conditions) { - this.delegatee = delegatee; + public DelegationTag(PublicKey delegator, String conditions) { + this.delegator = delegator; this.conditions = conditions == null ? "" : conditions; } @Transient public String getToken() { StringBuilder strToken = new StringBuilder(); - strToken.append("nostr:").append(getCode()).append(":").append(delegatee.toString()).append(":").append(conditions); + strToken.append("nostr:").append(getCode()).append(":").append(delegator.toString()).append(":").append(conditions); return strToken.toString(); } } diff --git a/nostr-event/src/main/java/nostr/event/tag/EventTag.java b/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java similarity index 62% rename from nostr-event/src/main/java/nostr/event/tag/EventTag.java rename to nostr-java-event/src/main/java/nostr/event/tag/EventTag.java index 15d7ebd51..2457548a3 100644 --- a/nostr-event/src/main/java/nostr/event/tag/EventTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java @@ -5,10 +5,15 @@ */ package nostr.event.tag; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import nostr.base.annotation.Key; import nostr.base.annotation.Tag; import nostr.event.BaseTag; @@ -23,15 +28,22 @@ @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @Tag(code = "e", name = "event") +@JsonPropertyOrder({"idEvent", "recommendedRelayUrl", "marker"}) +@NoArgsConstructor public class EventTag extends BaseTag { @Key + @JsonProperty("idEvent") private String idEvent; @Key + @JsonProperty("recommendedRelayUrl") + @JsonInclude(JsonInclude.Include.NON_NULL) private String recommendedRelayUrl; @Key(nip = 10) + @JsonProperty("marker") + @JsonInclude(JsonInclude.Include.NON_NULL) private Marker marker; public EventTag(String idEvent) { diff --git a/nostr-event/src/main/java/nostr/event/tag/NonceTag.java b/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java similarity index 69% rename from nostr-event/src/main/java/nostr/event/tag/NonceTag.java rename to nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java index 723c0882c..814443b6a 100644 --- a/nostr-event/src/main/java/nostr/event/tag/NonceTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java @@ -1,11 +1,14 @@ package nostr.event.tag; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import nostr.base.annotation.Key; import nostr.event.BaseTag; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.NonNull; import nostr.base.annotation.Tag; @@ -17,12 +20,16 @@ @Data @EqualsAndHashCode(callSuper = true) @Tag(code = "nonce", nip = 13) +@JsonPropertyOrder({"nonce", "difficulty"}) +@NoArgsConstructor public class NonceTag extends BaseTag { @Key + @JsonProperty("nonce") private Integer nonce; @Key + @JsonProperty("difficulty") private Integer difficulty; public NonceTag(@NonNull Integer nonce, @NonNull Integer difficulty) { diff --git a/nostr-event/src/main/java/nostr/event/tag/PubKeyTag.java b/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java similarity index 61% rename from nostr-event/src/main/java/nostr/event/tag/PubKeyTag.java rename to nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java index fc628b11a..8ed32e9e7 100644 --- a/nostr-event/src/main/java/nostr/event/tag/PubKeyTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java @@ -5,13 +5,14 @@ */ package nostr.event.tag; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.NonNull; import nostr.base.PublicKey; import nostr.base.annotation.Key; @@ -22,31 +23,31 @@ * * @author squirrel */ -@JsonPropertyOrder({"code", "pubKey"}) +@JsonPropertyOrder({"pubKey", "mainRelayUrl", "petName"}) @Builder @Data @EqualsAndHashCode(callSuper = true) @Tag(code = "p") +@NoArgsConstructor public class PubKeyTag extends BaseTag { - @JsonIgnore @Key + @JsonProperty("publicKey") private PublicKey publicKey; @Key + @JsonProperty("mainRelayUrl") + @JsonInclude(JsonInclude.Include.NON_NULL) private String mainRelayUrl; @Key(nip = 2) + @JsonProperty("petName") + @JsonInclude(JsonInclude.Include.NON_NULL) private String petName; - private PubKeyTag(@NonNull PublicKey publicKey, String mainRelayUrl, String petName) { + public PubKeyTag(@NonNull PublicKey publicKey, String mainRelayUrl, String petName) { this.publicKey = publicKey; this.mainRelayUrl = mainRelayUrl; this.petName = petName; } - - @JsonGetter("pubKey") - public String getStringPubKey() { - return publicKey.toString(); - } } diff --git a/nostr-event/src/main/java/nostr/event/tag/SubjectTag.java b/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java similarity index 56% rename from nostr-event/src/main/java/nostr/event/tag/SubjectTag.java rename to nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java index cedf2b599..8fd7466b8 100644 --- a/nostr-event/src/main/java/nostr/event/tag/SubjectTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java @@ -1,10 +1,13 @@ package nostr.event.tag; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NonNull; +import lombok.NoArgsConstructor; import nostr.base.annotation.Key; import nostr.base.annotation.Tag; import nostr.event.BaseTag; @@ -15,14 +18,14 @@ */ @Builder @Data +@NoArgsConstructor +@AllArgsConstructor @EqualsAndHashCode(callSuper = true) @Tag(code = "subject", nip = 14) +@JsonPropertyOrder({"subject"}) public final class SubjectTag extends BaseTag { @Key + @JsonProperty("subject") private String subject; - - public SubjectTag(@NonNull String subject) { - this.subject = subject; - } } diff --git a/nostr-event/src/main/java/nostr/event/util/Nip05Validator.java b/nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java similarity index 57% rename from nostr-event/src/main/java/nostr/event/util/Nip05Validator.java rename to nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java index 6f7bdcf56..5ffce4f8d 100644 --- a/nostr-event/src/main/java/nostr/event/util/Nip05Validator.java +++ b/nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java @@ -1,8 +1,5 @@ package nostr.event.util; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -10,13 +7,14 @@ import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; -import java.util.List; import java.util.Map; import java.util.logging.Level; import lombok.Builder; import lombok.Data; import lombok.extern.java.Log; import nostr.base.PublicKey; +import nostr.event.Nip05Content; +import nostr.event.json.codec.Nip05ContentDecoder; import nostr.util.NostrException; /** @@ -30,13 +28,15 @@ public class Nip05Validator { private final String nip05; private final PublicKey publicKey; + + private static final String LOCAL_PART_PATTERN = "^[a-zA-Z0-9-_\\.]+$"; public void validate() throws NostrException { if (this.nip05 != null) { var localPart = nip05.split("@")[0]; var domain = nip05.split("@")[1]; - if (!localPart.matches("^[-\\w.]+$")) { + if (!localPart.matches(LOCAL_PART_PATTERN)) { throw new NostrException("Invalid syntax in nip05 attribute."); } @@ -81,64 +81,19 @@ private void validatePublicKey(String domain, String localPart) throws Malformed throw new NostrException(String.format("Failed to connect to {0}. Error message: {1)", new Object[]{strUrl, connection.getResponseMessage()})); } - // TODO #30 - Use jackson - private String getPublicKey(StringBuilder content, String localPart) { - return Nip05Decoder.builder().jsonContent(content.toString()).build().getPublicKey(localPart).toString(); - } - - @Builder - private static class Nip05Decoder { + private String getPublicKey(StringBuilder content, String localPart) throws NostrException { - private final String jsonContent; + Nip05Content nip05Content = new Nip05ContentDecoder(content.toString()).decode(); - PublicKey getPublicKey(String localPart) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - Content content = objectMapper.readValue(this.jsonContent, Content.class); - - // Access the decoded data - Map names = content.getNames(); - for (Map.Entry entry : names.entrySet()) { - String name = entry.getKey(); - String hash = entry.getValue(); - if (name.equals(localPart)) { - return new PublicKey(hash); - } - } - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); + // Access the decoded data + Map names = nip05Content.getNames(); + for (Map.Entry entry : names.entrySet()) { + String name = entry.getKey(); + String hash = entry.getValue(); + if (name.equals(localPart)) { + return hash; } - - return null; } - - List getRelays(String localPart) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - Content content = objectMapper.readValue(this.jsonContent, Content.class); - - PublicKey pk = getPublicKey(localPart); - if (pk == null) { - return null; - } - - return content.getRelays().get(pk.toString()); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } - } - - @Data - private static class Content { - - @JsonProperty("names") - private Map names; - - @JsonProperty("relays") - private Map> relays; - - } - + return null; } - } diff --git a/nostr-examples/nb-configuration.xml b/nostr-java-examples/nb-configuration.xml similarity index 100% rename from nostr-examples/nb-configuration.xml rename to nostr-java-examples/nb-configuration.xml diff --git a/nostr-examples/nbactions.xml b/nostr-java-examples/nbactions.xml similarity index 100% rename from nostr-examples/nbactions.xml rename to nostr-java-examples/nbactions.xml diff --git a/nostr-examples/pom.xml b/nostr-java-examples/pom.xml similarity index 63% rename from nostr-examples/pom.xml rename to nostr-java-examples/pom.xml index 16e1ff35c..3e5e33c71 100644 --- a/nostr-examples/pom.xml +++ b/nostr-java-examples/pom.xml @@ -6,10 +6,10 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-examples + nostr-java-examples jar @@ -20,28 +20,32 @@ ${project.groupId} - nostr-event + nostr-java-event ${project.version} - - - ${project.groupId} - nostr-util - - ${project.groupId} - nostr-ws + nostr-java-ws ${project.version} ${project.groupId} - nostr-base + nostr-java-base ${project.version} ${project.groupId} - nostr-id + nostr-java-id + ${project.version} + + + ${project.groupId} + nostr-java-ws-handler-command-provider + ${project.version} + + + ${project.groupId} + nostr-java-client ${project.version} diff --git a/nostr-examples/src/main/java/module-info.java b/nostr-java-examples/src/main/java/module-info.java similarity index 97% rename from nostr-examples/src/main/java/module-info.java rename to nostr-java-examples/src/main/java/module-info.java index bec07cf9c..8ea342720 100644 --- a/nostr-examples/src/main/java/module-info.java +++ b/nostr-java-examples/src/main/java/module-info.java @@ -1,6 +1,7 @@ module nostr.examples { requires nostr.event; + requires nostr.client; requires static lombok; requires nostr.ws; requires org.eclipse.jetty.websocket.jetty.client; diff --git a/nostr-examples/src/main/java/nostr/examples/FilterRelays.java b/nostr-java-examples/src/main/java/nostr/examples/FilterRelays.java similarity index 100% rename from nostr-examples/src/main/java/nostr/examples/FilterRelays.java rename to nostr-java-examples/src/main/java/nostr/examples/FilterRelays.java diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrExamples.java new file mode 100644 index 000000000..4e1e413b6 --- /dev/null +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrExamples.java @@ -0,0 +1,652 @@ +package nostr.examples; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.LogManager; + +import lombok.extern.java.Log; +import nostr.base.Channel; +import nostr.base.ContentReason; +import nostr.base.UserProfile; +import nostr.base.PublicKey; +import nostr.event.BaseMessage; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.Marker; +import nostr.event.Reaction; +import nostr.event.impl.ChannelCreateEvent; +import nostr.event.impl.ChannelMessageEvent; +import nostr.event.impl.ChannelMetadataEvent; +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; +import nostr.event.impl.MentionsEvent; +import nostr.event.impl.MetadataEvent; +import nostr.event.impl.MuteUserEvent; +import nostr.event.impl.ReactionEvent; +import nostr.event.impl.ReplaceableEvent; +import nostr.event.impl.TextNoteEvent; +import nostr.event.list.KindList; +import nostr.event.message.EventMessage; +import nostr.event.message.ReqMessage; +import nostr.event.tag.EventTag; +import nostr.event.tag.PubKeyTag; +import nostr.client.Client; +import nostr.id.Identity; +import nostr.util.NostrException; +import nostr.util.UnsupportedNIPException; + +/** + * + * @author squirrel + */ +@Log +public class NostrExamples { + + private static final Identity RECEIVER = Identity.generateRandomIdentity(); + private static final Identity SENDER = Identity.generateRandomIdentity(); + + private static final UserProfile PROFILE = new UserProfile(SENDER.getPublicKey(), "erict875", "erict875@nostr-java.io", "It's me!", null); + + private final static Map RELAYS = Map.of("brb", "brb.io", "damus", "relay.damus.io", "ZBD", "nostr.zebedee.cloud", "taxi", "relay.taxi", "vision", "relay.nostr.vision"); + + private final static Client CLIENT = Client.getInstance(RELAYS); + + static { + final LogManager logManager = LogManager.getLogManager(); + try (final InputStream is = NostrExamples.class + .getResourceAsStream("/logging.properties")) { + logManager.readConfiguration(is); + } catch (IOException ex) { + System.exit(-1000); + } + + try { + PROFILE.setPicture(new URL("https://images.unsplash.com/photo-1462888210965-cdf193fb74de")); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws IOException, Exception { + try { +// Wait it until tried to connect to a half of relays + while (CLIENT.getThreadPool().getCompletedTaskCount() < (RELAYS.size() / 2)) { + Thread.sleep(5000); + } + + log.log(Level.FINE, "================= The Beginning"); + logAccountsData(); + + ExecutorService executor = Executors.newFixedThreadPool(10); + + executor.submit(() -> { + try { + sendTextNoteEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + sendEncryptedDirectMessage(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + mentionsEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + deletionEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + metaDataEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + ephemerealEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + reactionEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + replaceableEvent(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + internetIdMetadata(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + filters(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + createChannel(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + updateChannelMetadata(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + sendChannelMessage(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + hideMessage(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + executor.submit(() -> { + try { + muteUser(); + } catch (NostrException ex) { + log.log(Level.SEVERE, null, ex); + } + }); + + stop(executor); + + if (executor.isTerminated()) { + log.log(Level.FINE, "================== The End"); + } + + } catch (IllegalArgumentException ex) { + log.log(Level.SEVERE, null, ex); + throw new NostrException(ex); + } + } + + private static void sendTextNoteEvent() throws NostrException { + logHeader("sendTextNoteEvent"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + GenericEvent event = new TextNoteEvent(publicKeySender, tags, + "Hello world, I'm here on nostr-java API!"); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void sendEncryptedDirectMessage() throws NostrException { + logHeader("sendEncryptedDirectMessage"); + + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + var event2 = new DirectMessageEvent(publicKeySender, tags, "Hello Nakamoto!"); + + SENDER.encryptDirectMessage(event2); + SENDER.sign(event2); + + BaseMessage message = new EventMessage(event2); + + CLIENT.send(message); + + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void mentionsEvent() throws NostrException { + logHeader("mentionsEvent"); + + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).petName("nostr-java").build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + GenericEvent event = new MentionsEvent(publicKeySender, tags, "Hello " + RECEIVER.getPublicKey().toString()); + SENDER.sign(event); + + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void deletionEvent() throws NostrException { + logHeader("deletionEvent"); + + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).petName("nostr-java").build(); + List tags = new ArrayList<>(); + tags.add((PubKeyTag) rcptTag); + + GenericEvent event = new TextNoteEvent(publicKeySender, tags, "Hello Astral, Please delete me!"); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + tags = new ArrayList<>(); + tags.add(EventTag.builder().idEvent(event.getId()).build()); + GenericEvent delEvent = new DeletionEvent(publicKeySender, tags); + + SENDER.sign(delEvent); + message = new EventMessage(delEvent); + + CLIENT.send(message); + + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void metaDataEvent() throws NostrException { + logHeader("metaDataEvent"); + + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + var event = new MetadataEvent(publicKeySender, PROFILE); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void ephemerealEvent() throws NostrException { + logHeader("ephemerealEvent"); + + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).petName("nostr-java").build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + GenericEvent event = new EphemeralEvent(publicKeySender, Kind.EPHEMEREAL_EVENT.getValue(), tags); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void reactionEvent() throws NostrException { + logHeader("reactionEvent"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).petName("nostr-java").build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + GenericEvent event = new TextNoteEvent(publicKeySender, tags, "Hello Astral, Please like me!"); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + tags = new ArrayList<>(); + tags.add(EventTag.builder().idEvent(event.getId()).build()); + tags.add(PubKeyTag.builder().publicKey(publicKeySender).build()); + GenericEvent reactionEvent = new ReactionEvent(publicKeySender, tags, Reaction.LIKE, event); + + SENDER.sign(reactionEvent); + message = new EventMessage(reactionEvent); + + CLIENT.send(message); + + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void replaceableEvent() throws NostrException { + logHeader("replaceableEvent"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).petName("nostr-java").build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + GenericEvent event = new TextNoteEvent(publicKeySender, tags, "Hello Astral, Please replace me!"); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + tags = new ArrayList<>(); + tags.add(EventTag.builder().idEvent(event.getId()).build()); + GenericEvent replaceableEvent = new ReplaceableEvent(publicKeySender, 15000, tags, "Content"); + + SENDER.sign(replaceableEvent); + message = new EventMessage(replaceableEvent); + + CLIENT.send(message); + + replaceableEvent = new ReplaceableEvent(publicKeySender, 15000, tags, "New Content"); + + SENDER.sign(replaceableEvent); + message = new EventMessage(replaceableEvent); + + CLIENT.send(message); + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + private static void internetIdMetadata() throws NostrException { + logHeader("internetIdMetadata"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + PubKeyTag rcptTag = PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).petName("nostr-java").build(); + List tags = new ArrayList<>(); + tags.add(rcptTag); + + GenericEvent event = new InternetIdentifierMetadataEvent(publicKeySender, tags, PROFILE); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + } catch (UnsupportedNIPException ex) { + log.log(Level.WARNING, null, ex); + } + } + + // FIXME + public static void filters() throws NostrException { + logHeader("filters"); + try { + KindList kindList = new KindList(); + kindList.add(Kind.EPHEMEREAL_EVENT); + kindList.add(Kind.TEXT_NOTE); + + Filters filters = Filters.builder().kinds(kindList).limit(10).build(); + + String subId = "subId" + System.currentTimeMillis(); + BaseMessage message = new ReqMessage(subId, filters); + + CLIENT.send(message); + } catch (Exception ex) { + throw new NostrException(ex); + } + } + + private static GenericEvent createChannel() throws NostrException { + logHeader("createChannel"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + var channel = Channel.builder().name("JNostr Channel") + .about("This is a channel to test NIP28 in nostr-java") + .picture("https://cdn.pixabay.com/photo/2020/05/19/13/48/cartoon-5190942_960_720.jpg").build(); + GenericEvent event = new ChannelCreateEvent(publicKeySender, new ArrayList(), channel.toString()); + + SENDER.sign(event); + BaseMessage message = new EventMessage(event); + + CLIENT.send(message); + + return event; + } catch (NostrException ex) { + throw new NostrException(ex); + } + } + + private static void updateChannelMetadata() throws NostrException { + logHeader("updateChannelMetadata"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + var channelCreateEvent = createChannel(); + + var tags = new ArrayList(); + tags.add(EventTag.builder().idEvent(channelCreateEvent.getId()) + .recommendedRelayUrl(CLIENT.getRelays().stream().findFirst().get().getUri()).build()); + + var channel = Channel.builder().name("test change name") + .about("This is a channel to test NIP28 in nostr-java | changed") + .picture("https://cdn.pixabay.com/photo/2020/05/19/13/48/cartoon-5190942_960_720.jpg").build(); + GenericEvent event = new ChannelMetadataEvent(publicKeySender, tags, channel.toString()); + + SENDER.sign(event); + var message = new EventMessage(event); + + CLIENT.send(message); + } catch (Exception ex) { + throw new NostrException(ex); + } + } + + private static GenericEvent sendChannelMessage() throws NostrException { + logHeader("sendChannelMessage"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + var channelCreateEvent = createChannel(); + + var tags = new ArrayList(); + tags.add(EventTag.builder().idEvent(channelCreateEvent.getId()) + .recommendedRelayUrl(CLIENT.getRelays().stream().findFirst().get().getUri()) + .marker(Marker.ROOT) + .build()); + + GenericEvent event = new ChannelMessageEvent(publicKeySender, tags, "Hello everybody!"); + + SENDER.sign(event); + var message = new EventMessage(event); + + CLIENT.send(message); + + return event; + } catch (NostrException ex) { + throw new NostrException(ex); + } + } + + private static GenericEvent hideMessage() throws NostrException { + logHeader("hideMessage"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + var channelMessageEvent = sendChannelMessage(); + + var tags = new ArrayList(); + tags.add(EventTag.builder().idEvent(channelMessageEvent.getId()).build()); + + GenericEvent event = new HideMessageEvent(publicKeySender, tags, + ContentReason.builder().reason("Dick pic").build().toString()); + + SENDER.sign(event); + var message = new EventMessage(event); + + CLIENT.send(message); + + return event; + } catch (NostrException ex) { + throw new NostrException(ex); + } + } + + private static GenericEvent muteUser() throws NostrException { + logHeader("muteUser"); + try { + final PublicKey publicKeySender = SENDER.getPublicKey(); + + var tags = new ArrayList(); + tags.add(PubKeyTag.builder().publicKey(RECEIVER.getPublicKey()).build()); + + GenericEvent event = new MuteUserEvent(publicKeySender, tags, + ContentReason.builder().reason("Posting dick pics").build().toString()); + + SENDER.sign(event); + var message = new EventMessage(event); + + CLIENT.send(message); + + return event; + } catch (NostrException ex) { + throw new NostrException(ex); + } + } + +// public static void sensitiveContentNote(Identity wallet, Client cliepublicKeySendernt) throws NostrException { +// logHeader("sensitiveContentNote"); +// try { +// // Create the attribute value list +// List values = new ArrayList<>(); +// values.add("sensitive content"); +// +// // Create the attributes +// final ElementAttribute attr = ElementAttribute.builder().nip(36).isString(true).name("reason").valueList(values).build(); +// Set attributes = new HashSet<>(); +// attributes.add(attr); +// +// GenericTag sensitiveContentTag = new GenericTag(1, "", attributes); +// } catch (UnsupportedNIPException ex) { +// log.log(Level.WARNING, null, ex); +// } catch (Exception ex) { +// throw new NostrException(ex); +// } +// +// } + private static void logAccountsData() throws NostrException { + StringBuilder msg = new StringBuilder("################################ ACCOUNTS BEGINNING ################################") + .append('\n').append("*** RECEIVER ***").append('\n') + .append('\n').append("* PrivateKey: ").append(RECEIVER.getPrivateKey().getBech32()) + .append('\n').append("* PrivateKey HEX: ").append(RECEIVER.getPrivateKey().toString()) + .append('\n').append("* PublicKey: ").append(RECEIVER.getPublicKey().getBech32()) + .append('\n').append("* PublicKey HEX: ").append(RECEIVER.getPublicKey().toString()) + .append('\n').append('\n').append("*** SENDER ***").append('\n') + .append('\n').append("* PrivateKey: ").append(SENDER.getPrivateKey().getBech32()) + .append('\n').append("* PrivateKey HEX: ").append(SENDER.getPrivateKey().toString()) + .append('\n').append("* PublicKey: ").append(SENDER.getPublicKey().getBech32()) + .append('\n').append("* PublicKey HEX: ").append(SENDER.getPublicKey().toString()) + .append('\n').append('\n').append("################################ ACCOUNTS END ################################"); + + log.log(Level.INFO, msg.toString()); + } + + private static void logHeader(String header) { + for (int i = 0; i < 30; i++) { + System.out.print("#"); + } + System.out.println(); + System.out.println("\t" + header); + for (int i = 0; i < 30; i++) { + System.out.print("#"); + } + System.out.println(); + } + + private static void stop(ExecutorService executor) { + try { + executor.shutdown(); + executor.awaitTermination(60, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.log(Level.SEVERE, "termination interrupted"); + } finally { + if (!executor.isTerminated()) { + log.log(Level.SEVERE, "killing non-finished tasks"); + } + executor.shutdownNow(); + } + } +} diff --git a/nostr-examples/src/main/resources/logging.properties b/nostr-java-examples/src/main/resources/logging.properties similarity index 100% rename from nostr-examples/src/main/resources/logging.properties rename to nostr-java-examples/src/main/resources/logging.properties diff --git a/nostr-id/nb-configuration.xml b/nostr-java-id/nb-configuration.xml similarity index 100% rename from nostr-id/nb-configuration.xml rename to nostr-java-id/nb-configuration.xml diff --git a/nostr-id/pom.xml b/nostr-java-id/pom.xml similarity index 69% rename from nostr-id/pom.xml rename to nostr-java-id/pom.xml index 1b7cac3c4..4860d2f29 100644 --- a/nostr-id/pom.xml +++ b/nostr-java-id/pom.xml @@ -6,10 +6,10 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-id + nostr-java-id jar @@ -26,22 +26,17 @@ ${project.groupId} - nostr-base + nostr-java-base ${project.version} ${project.groupId} - nostr-event + nostr-java-event ${project.version} ${project.groupId} - nostr-util - ${project.version} - - - ${project.groupId} - nostr-ws + nostr-java-util ${project.version} @@ -51,12 +46,7 @@ ${project.groupId} - nostr-ws-handler-interface - ${project.version} - - - ${project.groupId} - nostr-ws-request-handler-provider + nostr-java-crypto ${project.version} diff --git a/nostr-java-id/src/main/java/module-info.java b/nostr-java-id/src/main/java/module-info.java new file mode 100644 index 000000000..f779e52fd --- /dev/null +++ b/nostr-java-id/src/main/java/module-info.java @@ -0,0 +1,16 @@ + +module nostr.id { + requires static lombok; + requires nostr.base; + requires nostr.crypto; + requires nostr.event; + requires nostr.util; + requires com.fasterxml.jackson.databind; + requires com.fasterxml.jackson.annotation; + requires com.fasterxml.jackson.core; + requires org.bouncycastle.provider; + requires java.logging; + requires java.desktop; + + exports nostr.id; +} diff --git a/nostr-id/src/main/java/nostr/id/Identity.java b/nostr-java-id/src/main/java/nostr/id/Identity.java similarity index 88% rename from nostr-id/src/main/java/nostr/id/Identity.java rename to nostr-java-id/src/main/java/nostr/id/Identity.java index 8c31d1ade..41c3ab26b 100644 --- a/nostr-id/src/main/java/nostr/id/Identity.java +++ b/nostr-java-id/src/main/java/nostr/id/Identity.java @@ -27,7 +27,7 @@ import lombok.NonNull; import lombok.ToString; import lombok.extern.java.Log; -import nostr.base.BaseConfiguration; +import nostr.util.AbstractBaseConfiguration; import nostr.base.ISignable; import nostr.base.ITag; import nostr.base.PrivateKey; @@ -53,19 +53,23 @@ public class Identity { private static Identity INSTANCE; - + @ToString.Exclude private final PrivateKey privateKey; - private Identity(String profileFile) throws IOException, NostrException { - this.privateKey = new IdentityConfiguration(profileFile).getPrivateKey(); + private Identity() throws IOException, NostrException { + this.privateKey = new IdentityConfiguration().getPrivateKey(); } - - public static Identity getInstance() throws IOException, NostrException { - if(INSTANCE == null) { - INSTANCE = new Identity("/profile.properties"); + + public static Identity getInstance() { + if (INSTANCE == null) { + try { + INSTANCE = new Identity(); + } catch (IOException | NostrException ex) { + throw new RuntimeException(ex); + } } - + return INSTANCE; } @@ -78,7 +82,7 @@ public PublicKey getPublicKey() { } public void encryptDirectMessage(@NonNull DirectMessageEvent dmEvent) throws NostrException { - ITag pkTag = (ITag) dmEvent.getTags().getList().get(0); + ITag pkTag = (ITag) dmEvent.getTags().get(0); if (pkTag instanceof PubKeyTag pubKeyTag) { try { var rcptPublicKey = pubKeyTag.getPublicKey(); @@ -138,14 +142,18 @@ private Signature signEvent(@NonNull GenericEvent event) throws NoSuchAlgorithmE event.update(); log.log(Level.FINER, "Serialized event: {0}", new String(event.get_serializedEvent())); final var signedHashedSerializedEvent = Schnorr.sign(NostrUtil.sha256(event.get_serializedEvent()), privateKey.getRawData(), generateAuxRand()); - final Signature signature = Signature.builder().rawData(signedHashedSerializedEvent).pubKey(this.getPublicKey()).build(); + final Signature signature = new Signature(); + signature.setRawData(signedHashedSerializedEvent); + signature.setPubKey(getPublicKey()); event.setSignature(signature); return signature; } private Signature signDelegationTag(@NonNull DelegationTag delegationTag) throws NoSuchAlgorithmException, IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException, Exception { final var signedHashedToken = Schnorr.sign(NostrUtil.sha256(delegationTag.getToken().getBytes(StandardCharsets.UTF_8)), privateKey.getRawData(), generateAuxRand()); - final Signature signature = Signature.builder().rawData(signedHashedToken).pubKey(this.getPublicKey()).build(); + final Signature signature = new Signature(); + signature.setRawData(signedHashedToken); + signature.setPubKey(getPublicKey()); delegationTag.setSignature(signature); return signature; } @@ -227,22 +235,28 @@ private static PublicKey generatePublicKey(PrivateKey privateKey) throws Excepti } @Log - static class IdentityConfiguration extends BaseConfiguration { + static class IdentityConfiguration extends AbstractBaseConfiguration { - IdentityConfiguration(String propertyFile) throws IOException { - super(propertyFile); + IdentityConfiguration() throws IOException { + super(); + var configFile = appConfig.getIdentityProperties(); + configFile = configFile.startsWith("/") ? configFile : "/" + configFile; + load(configFile); } PrivateKey getPrivateKey() throws IOException, NostrException { String privKey = getProperty("privateKey"); log.log(Level.FINE, "Reading the private key..."); + if (privKey == null) { + throw new RuntimeException("Missing private key. Aborting...."); + } String hex = privKey.startsWith(Bech32Prefix.NSEC.getCode()) ? Bech32.fromBech32(privKey) : privKey; return new PrivateKey(hex); } PublicKey getPublicKey() throws NostrException, IOException { - String pubKey = getProperty("publickKey"); + String pubKey = getProperty("publicKey"); if (pubKey == null || "".equals(pubKey.trim())) { log.log(Level.FINE, "Generating new public key"); try { diff --git a/nostr-id/src/main/resources/profile.properties b/nostr-java-id/src/main/resources/profile.properties similarity index 50% rename from nostr-id/src/main/resources/profile.properties rename to nostr-java-id/src/main/resources/profile.properties index e44cfe668..564483683 100644 --- a/nostr-id/src/main/resources/profile.properties +++ b/nostr-java-id/src/main/resources/profile.properties @@ -1,3 +1,3 @@ privateKey=7b94cef920b07392577940df7181935189e85e0a90bbe474e137770671473ce7 -publickKey=f6a04a16b1fb3b4bf40838dacc7f8bd4d46b60d3c9e2a4915877f9a2eac8e323 \ No newline at end of file +publicKey=f6a04a16b1fb3b4bf40838dacc7f8bd4d46b60d3c9e2a4915877f9a2eac8e323 \ No newline at end of file diff --git a/nostr-id/src/main/resources/relays.properties b/nostr-java-id/src/main/resources/relays.properties similarity index 100% rename from nostr-id/src/main/resources/relays.properties rename to nostr-java-id/src/main/resources/relays.properties diff --git a/nostr-test/nb-configuration.xml b/nostr-java-test/nb-configuration.xml similarity index 100% rename from nostr-test/nb-configuration.xml rename to nostr-java-test/nb-configuration.xml diff --git a/nostr-test/pom.xml b/nostr-java-test/pom.xml similarity index 81% rename from nostr-test/pom.xml rename to nostr-java-test/pom.xml index fb751e521..e4d21bb3c 100644 --- a/nostr-test/pom.xml +++ b/nostr-java-test/pom.xml @@ -6,16 +6,12 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-test + nostr-java-test jar - - com.tcheeric.nostr.test.NostrTest - - @@ -26,23 +22,23 @@ ${project.groupId} - nostr-event + nostr-java-event ${project.version} ${project.groupId} - nostr-base + nostr-java-base ${project.version} ${project.groupId} - nostr-id + nostr-java-id ${project.version} test ${project.groupId} - nostr-util + nostr-java-util ${project.version} @@ -51,6 +47,11 @@ org.junit.jupiter junit-jupiter-engine + + ${project.groupId} + nostr-java-client + ${project.version} + diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java new file mode 100644 index 000000000..0c895717f --- /dev/null +++ b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java @@ -0,0 +1,220 @@ +package nostr.test; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import lombok.extern.java.Log; +import nostr.base.ElementAttribute; +import nostr.base.GenericTagQuery; +import nostr.base.IEvent; +import nostr.base.UserProfile; +import nostr.base.PublicKey; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.Reaction; +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; +import nostr.event.impl.MentionsEvent; +import nostr.event.impl.MetadataEvent; +import nostr.event.impl.OtsEvent; +import nostr.event.impl.ReactionEvent; +import nostr.event.impl.ReplaceableEvent; +import nostr.event.impl.TextNoteEvent; +import nostr.event.list.EventList; +import nostr.event.list.GenericTagQueryList; +import nostr.event.list.KindList; +import nostr.event.list.PublicKeyList; +import nostr.event.tag.PubKeyTag; +import nostr.util.NostrException; + +/** + * + * @author squirrel + */ +@Log +//TODO - Add the sender PK to all createEvents. +public class EntityFactory { + + @Log + public static class Events { + + @SuppressWarnings("unchecked") + public static EphemeralEvent createEphemeralEvent(PublicKey publicKey) { + List tagList = new ArrayList<>(); + tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("eric").build()); + GenericEvent event = new EphemeralEvent(publicKey, Kind.EPHEMEREAL_EVENT.getValue(), tagList); + event.update(); + return (EphemeralEvent) event; + } + + public static DirectMessageEvent createDirectMessageEvent(PublicKey senderPublicKey, PublicKey rcptPublicKey, String content) { + List tagList = new ArrayList<>(); + tagList.add(PubKeyTag.builder().publicKey(rcptPublicKey).petName("uq7yfx3l").build()); + GenericEvent event = new DirectMessageEvent(senderPublicKey, tagList, content); + event.update(); + return (DirectMessageEvent) event; + } + + public static Filters createFilters(PublicKeyList authors, KindList kindList, Long since) { + return Filters.builder().authors(authors).kinds(kindList).since(since).build(); + } + + @SuppressWarnings("unchecked") + public static InternetIdentifierMetadataEvent createInternetIdentifierMetadataEvent(UserProfile profile) throws NostrException { + final PublicKey publicKey = profile.getPublicKey(); + List tagList = new ArrayList<>(); + tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("daniel").build()); + GenericEvent event = new InternetIdentifierMetadataEvent(publicKey, tagList, profile); + event.update(); + return (InternetIdentifierMetadataEvent) event; + } + + @SuppressWarnings("unchecked") + public static MentionsEvent createMentionsEvent(PublicKey publicKey) { + List tagList = new ArrayList<>(); + tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("charlie").build()); + String content = generateRamdomAlpha(32); + StringBuilder sbContent = new StringBuilder(content); + + int len = tagList.size(); + for (int i = 0; i < len; i++) { + sbContent.append(", ").append(((PubKeyTag) tagList.get(i)).getPublicKey().toString()); + + } + GenericEvent event = new MentionsEvent(publicKey, tagList, sbContent.toString()); + event.update(); + return (MentionsEvent) event; + } + + @SuppressWarnings("unchecked") + public static MetadataEvent createMetadataEvent(UserProfile profile) throws NostrException { + final PublicKey publicKey = profile.getPublicKey(); + GenericEvent event = new MetadataEvent(publicKey, profile); + return (MetadataEvent) event; + } + + @SuppressWarnings("unchecked") + public static ReactionEvent createReactionEvent(PublicKey publicKey, GenericEvent original) { + List tagList = new ArrayList<>(); + tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("charlie").build()); + GenericEvent event = new ReactionEvent(publicKey, tagList, Reaction.LIKE, original); + return (ReactionEvent) event; + } + + public static ReplaceableEvent createReplaceableEvent(PublicKey publicKey) { + String content = generateRamdomAlpha(32); + GenericEvent event = new ReplaceableEvent(publicKey, 15000, new ArrayList<>(), content); + return (ReplaceableEvent) event; + } + + public static TextNoteEvent createTextNoteEvent(PublicKey publicKey) { + String content = generateRamdomAlpha(32); + return createTextNoteEvent(publicKey, content); + } + + public static TextNoteEvent createTextNoteEvent(PublicKey publicKey, String content) { + List tagList = new ArrayList<>(); + tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("alice").build()); + GenericEvent event = new TextNoteEvent(publicKey, tagList, content); + return (TextNoteEvent) event; + } + + public static OtsEvent createOtsEvent(PublicKey publicKey) { + List tagList = new ArrayList<>(); + final PubKeyTag pkTag = PubKeyTag.builder().publicKey(publicKey).petName("bob").build(); + tagList.add(pkTag); + OtsEvent event = new OtsEvent(publicKey, tagList, generateRamdomAlpha(32), generateRamdomAlpha(32)); + return event; + } + + public static GenericTag createGenericTag(PublicKey publicKey) { + IEvent event = createTextNoteEvent(publicKey); + return createGenericTag(publicKey, event); + } + + public static GenericTag createGenericTag(PublicKey publicKey, IEvent event) { + GenericTag tag = new GenericTag("devil"); + tag.addAttribute(ElementAttribute.builder().value("Lucifer").nip(666).build()); + ((GenericEvent) event).addTag(tag); + return tag; + } + + public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Integer tagNip) { + GenericTag tag = new GenericTag("devil", tagNip); + tag.addAttribute(ElementAttribute.builder().value("Lucifer").nip(666).build()); + ((GenericEvent) event).addTag(tag); + return tag; + } + + public static Filters createFilters(PublicKey publicKey) { + EventList eventList = new EventList(); + eventList.add(createTextNoteEvent(publicKey)); + eventList.add(createEphemeralEvent(publicKey)); + + EventList refEvents = new EventList(); + refEvents.add(createTextNoteEvent(publicKey)); + + GenericTagQueryList gtqList = new GenericTagQueryList(); + gtqList.add(createGenericTagQuery()); + + return Filters.builder().events(eventList).referencedEvents(refEvents).genericTagQueryList(gtqList).build(); + } + + public static GenericTagQuery createGenericTagQuery() { + Character c = generateRamdomAlpha(1).charAt(0); + String v1 = generateRamdomAlpha(5); + String v2 = generateRamdomAlpha(6); + String v3 = generateRamdomAlpha(7); + + List list = new ArrayList<>(); + list.add(v3); + list.add(v2); + list.add(v1); + + var result = new GenericTagQuery(); + result.setTagName(c); + result.setValue(list); + return result; + } + } + + public static UserProfile createProfile(PublicKey pubKey) { + try { + String number = EntityFactory.generateRandomNumber(4); + String about = "about_" + number; + String name = "name_" + number; + String nip05 = name + "@tcheeric.com"; + String url = "http://assets.tcheeric.com/" + number + ".PNG"; + + return new UserProfile(pubKey, name, nip05, about, new URL(url)); + + } catch (MalformedURLException ex) { + throw new RuntimeException(ex); + } + } + + public static String generateRamdomAlpha(int len) { + return generateRandom(58, 122, len); + } + + public static String generateRandomNumber(int len) { + return generateRandom(48, 57, len); + } + + private static String generateRandom(int leftLimit, int rightLimit, int len) { + + return new Random().ints(leftLimit, rightLimit + 1) + .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) + .limit(len) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } + +} diff --git a/nostr-test/src/test/java/nostr/test/base/NostrUtilTest.java b/nostr-java-test/src/test/java/nostr/test/base/NostrUtilTest.java similarity index 100% rename from nostr-test/src/test/java/nostr/test/base/NostrUtilTest.java rename to nostr-java-test/src/test/java/nostr/test/base/NostrUtilTest.java diff --git a/nostr-test/src/test/java/nostr/test/crypto/CryptoTest.java b/nostr-java-test/src/test/java/nostr/test/crypto/CryptoTest.java similarity index 100% rename from nostr-test/src/test/java/nostr/test/crypto/CryptoTest.java rename to nostr-java-test/src/test/java/nostr/test/crypto/CryptoTest.java 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 new file mode 100644 index 000000000..a7b90d4c2 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java @@ -0,0 +1,86 @@ +package nostr.test.event; + +import nostr.base.PublicKey; +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 nostr.util.NostrException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import nostr.event.BaseMessage; + +public class DecodeTest { + + @Test + public void decodeTest() throws NostrException { + + String json = "[" + + "\"EVENT\"," + + "\"temp20230627\"," + + "{" + + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," + + "\"kind\":1," + + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," + + "\"created_at\":1687765220," + + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," + + "\"tags\":[" + + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," + + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" + + "]," + + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" + + "}]"; + + BaseMessageDecoder decoder = new BaseMessageDecoder(json); + BaseMessage message = decoder.decode(); + + 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 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(); + } + + } + + } + +} diff --git a/nostr-test/src/test/java/nostr/test/event/EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/EventTest.java similarity index 60% rename from nostr-test/src/test/java/nostr/test/event/EventTest.java rename to nostr-java-test/src/test/java/nostr/test/event/EventTest.java index 62a163fba..40c5edbd8 100644 --- a/nostr-test/src/test/java/nostr/test/event/EventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/EventTest.java @@ -1,20 +1,25 @@ package nostr.test.event; +import com.fasterxml.jackson.core.JsonProcessingException; import java.io.IOException; +import nostr.base.ElementAttribute; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import nostr.base.ElementAttribute; +import nostr.base.IEncoder; import nostr.base.IEvent; import nostr.base.PublicKey; import nostr.base.Relay; import nostr.crypto.bech32.Bech32; import nostr.crypto.bech32.Bech32Prefix; +import nostr.event.BaseEvent; +import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericMessage; import nostr.event.impl.GenericTag; -import nostr.event.marshaller.impl.ElementMarshaller; +import nostr.event.json.codec.BaseEventEncoder; +import nostr.event.json.codec.BaseTagEncoder; import nostr.event.util.Nip05Validator; import nostr.id.Identity; import nostr.test.EntityFactory; @@ -27,69 +32,50 @@ * @author squirrel */ public class EventTest { - - //private final Identity identity; + public EventTest() throws IOException, NostrException { - //this.identity = new Identity("/profile.properties"); } @Test - public void testCreateTextNoteEvent() { + public void testCreateTextNoteEvent() throws NostrException { + System.out.println("testCreateTextNoteEvent"); + PublicKey publicKey = Identity.getInstance().getPublicKey(); + GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); + instance.update(); + Assertions.assertNotNull(instance.getId()); + Assertions.assertNotNull(instance.getCreatedAt()); + Assertions.assertNull(instance.getSignature()); + final String bech32 = instance.toBech32(); + Assertions.assertNotNull(bech32); + Assertions.assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); + } + + @Test + public void testCreateGenericTag() throws JsonProcessingException { try { - System.out.println("testCreateTextNoteEvent"); - //PublicKey publicKey = this.identity.getPublicKey(); + System.out.println("testCreateGenericTag"); PublicKey publicKey = Identity.getInstance().getPublicKey(); - GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); - Assertions.assertNotNull(instance.getId()); - Assertions.assertNotNull(instance.getCreatedAt()); - Assertions.assertNull(instance.getSignature()); - final String bech32 = instance.toBech32(); - Assertions.assertNotNull(bech32); - Assertions.assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); - } catch (NostrException | IOException ex) { + GenericTag genericTag = EntityFactory.Events.createGenericTag(publicKey); + + Relay relay = Relay.builder().uri("wss://secret.relay.com").build(); + relay.addNipSupport(1); + relay.addNipSupport(genericTag.getNip()); + var attrs = genericTag.getAttributes(); + for (var a : attrs) { + relay.addNipSupport(a.getNip()); + } + + var encoder = new BaseTagEncoder((BaseTag)genericTag, relay); + var strJsonEvent = encoder.encode(); + + var tag = IEncoder.MAPPER.readValue(strJsonEvent, BaseTag.class); + Assertions.assertEquals(genericTag, tag); + + } catch (NostrException ex) { Assertions.fail(ex); } } - @Test - public void testCreateGenericTag() { -// try { -// System.out.println("testCreateGenericTag"); -// PublicKey publicKey = this.identity.getPublicKey(); -// GenericTag genericTag = EntityFactory.Events.createGenericTag(publicKey); -// -// Relay relay = Relay.builder().uri("wss://secret.relay.com").build(); -// relay.addNipSupport(1); -// relay.addNipSupport(genericTag.getNip()); -// var attrs = genericTag.getAttributes(); -// for (var a : attrs) { -// relay.addNipSupport(a.getNip()); -// } -// -// ElementMarshaller marshaller = new ElementMarshaller(genericTag.getParent(), relay); -// var strJsonEvent = marshaller.marshall(); -// -// var jsonValue = new JsonObjectUnmarshaller(strJsonEvent).unmarshall(); -// -// IValue tags = ((ObjectValue) jsonValue).get("tags").get(); -// -// Assertions.assertEquals(2, ((ArrayValue) tags).length()); -// -// IValue tag = ((ArrayValue) tags).get(1).get(); -// -// Assertions.assertTrue(tag instanceof ArrayValue); -// -// IValue code = ((ArrayValue) tag).get(0).get(); -// -// Assertions.assertTrue(code instanceof StringValue); -// -// Assertions.assertEquals("devil", code.getValue()); -// -// } catch (NostrException ex) { -// Assertions.fail(ex); -// } - } - @Test public void testCreateUnsupportedGenericTagAttribute() { // try { @@ -101,7 +87,7 @@ public void testCreateUnsupportedGenericTagAttribute() { // relay.addNipSupport(1); // relay.addNipSupport(genericTag.getNip()); // -// ElementMarshaller marshaller = new ElementMarshaller(genericTag.getParent(), relay); +// BaseEventEncoder marshaller = new BaseEventEncoder(genericTag.getParent(), relay); // var strJsonEvent = marshaller.marshall(); // // var jsonValue = new JsonObjectUnmarshaller(strJsonEvent).unmarshall(); @@ -137,11 +123,11 @@ public void testCreateUnsupportedGenericTag() throws IOException, NostrException Relay relay = Relay.builder().uri("wss://secret.relay.com").build(); relay.addNipSupport(0); - ElementMarshaller marshaller = new ElementMarshaller(genericTag.getParent(), relay); + var encoder = new BaseEventEncoder((BaseEvent) genericTag.getParent(), relay); UnsupportedNIPException thrown = Assertions.assertThrows(UnsupportedNIPException.class, () -> { - marshaller.marshall(); + encoder.encode(); }, "This event is not supported. List of relay supported NIP(s): " + relay.printSupportedNips() ); @@ -156,7 +142,7 @@ public void testNip05Validator() { var nip05 = "erict875@getalby.com"; var publicKey = new PublicKey(NostrUtil.hexToBytes(Bech32.fromBech32("npub126klq89p42wk78p4j5ur8wlxmxdqepdh8tez9e4axpd4run5nahsmff27j"))); - Nip05Validator nip05Validator = Nip05Validator.builder().nip05(nip05).publicKey(publicKey).build(); + var nip05Validator = Nip05Validator.builder().nip05(nip05).publicKey(publicKey).build(); nip05Validator.validate(); } catch (NostrException ex) { diff --git a/nostr-test/src/test/java/nostr/test/id/ClientTest.java b/nostr-java-test/src/test/java/nostr/test/id/ClientTest.java similarity index 60% rename from nostr-test/src/test/java/nostr/test/id/ClientTest.java rename to nostr-java-test/src/test/java/nostr/test/id/ClientTest.java index 3b90ba8e8..29eab85d3 100644 --- a/nostr-test/src/test/java/nostr/test/id/ClientTest.java +++ b/nostr-java-test/src/test/java/nostr/test/id/ClientTest.java @@ -1,16 +1,15 @@ package nostr.test.id; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import org.junit.jupiter.api.Test; import nostr.base.PublicKey; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; import nostr.event.message.EventMessage; -import nostr.id.Client; +import nostr.client.Client; import nostr.id.Identity; import nostr.test.EntityFactory; import nostr.util.NostrException; @@ -21,25 +20,16 @@ */ class ClientTest { -// private final Client client; -// private final Identity identity; - public ClientTest() throws IOException, NostrException { -// this.client = new Client("/relays.properties"); -// this.identity = new Identity("/profile.properties"); } @Test public void testSend() { - try { - System.out.println("testSend"); - PublicKey publicKey = Identity.getInstance().getPublicKey(); - GenericMessage msg = new EventMessage(EntityFactory.Events.createTextNoteEvent(publicKey)); - Client.getInstance("/relays.properties").send(msg); - assertTrue(true); - } catch (IOException | NostrException ex) { - fail(ex); - } + System.out.println("testSend"); + PublicKey publicKey = Identity.getInstance().getPublicKey(); + BaseMessage msg = new EventMessage(EntityFactory.Events.createTextNoteEvent(publicKey)); + Client.getInstance().send(msg); + assertTrue(true); } // @Test diff --git a/nostr-test/src/test/java/nostr/test/id/IdentityTest.java b/nostr-java-test/src/test/java/nostr/test/id/IdentityTest.java similarity index 97% rename from nostr-test/src/test/java/nostr/test/id/IdentityTest.java rename to nostr-java-test/src/test/java/nostr/test/id/IdentityTest.java index 83d4a5f88..137f8e933 100644 --- a/nostr-test/src/test/java/nostr/test/id/IdentityTest.java +++ b/nostr-java-test/src/test/java/nostr/test/id/IdentityTest.java @@ -23,7 +23,6 @@ public class IdentityTest { //private final Identity identity; public IdentityTest() throws IOException, NostrException { - //Identity.getInstance() = new Identity("/profile.properties"); } @Test 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 new file mode 100644 index 000000000..3b464ee4d --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java @@ -0,0 +1,135 @@ +package nostr.test.json; + +import nostr.base.Command; +import nostr.base.ElementAttribute; +import nostr.base.PublicKey; +import nostr.crypto.bech32.Bech32; +import nostr.event.BaseMessage; +import nostr.event.BaseTag; +import nostr.event.Marker; +import nostr.event.impl.Filters; +import nostr.event.impl.GenericEvent; +import nostr.event.impl.GenericTag; +import nostr.event.json.codec.BaseMessageDecoder; +import nostr.event.json.codec.GenericTagDecoder; +import nostr.event.json.codec.BaseTagDecoder; +import nostr.event.json.codec.FiltersDecoder; +import nostr.event.json.codec.FiltersEncoder; +import nostr.event.message.EventMessage; +import nostr.event.tag.EventTag; +import nostr.event.tag.PubKeyTag; +import nostr.test.EntityFactory; +import nostr.util.NostrException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * + * @author eric + */ +public class JsonParseTest { + + @Test + public void testBaseMessageDecoder() throws NostrException { + System.out.println("testBaseMessageDecoder"); + + final String parseTarget + = "[\"EVENT\"," + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\"," + + "{" + + "\"content\":\"直んないわ。まあええか\"," + + "\"created_at\":1686199583," + + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," + + "\"kind\":1," + + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," + + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," + + "\"tags\":[]" + + "}]"; + + final var message = new BaseMessageDecoder(parseTarget).decode(); + + Assertions.assertEquals(Command.EVENT.toString(), message.getCommand()); + + final var event = (GenericEvent) (((EventMessage) message).getEvent()); + Assertions.assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((EventMessage) message).getSubscriptionId()); + Assertions.assertEquals(1, event.getKind().intValue()); + Assertions.assertEquals(1686199583, event.getCreatedAt().longValue()); + Assertions.assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId()); + } + + @Test + public void testBaseMessageMarkerDecoder() throws NostrException { + System.out.println("testBaseMessageMarkerDecoder"); + + String json = "[" + + "\"EVENT\"," + + "\"temp20230627\"," + + "{" + + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," + + "\"kind\":1," + + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," + + "\"created_at\":1687765220," + + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," + + "\"tags\":[" + + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," + + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" + + "]," + + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" + + "}]"; + + BaseMessageDecoder decoder = new BaseMessageDecoder(json); + BaseMessage message = decoder.decode(); + + final var event = (GenericEvent) (((EventMessage) message).getEvent()); + var tags = event.getTags(); + for (BaseTag t : tags) { + if (t.getCode().equalsIgnoreCase("e")) { + EventTag et = (EventTag) t; + Assertions.assertEquals(Marker.ROOT, et.getMarker()); + } + } + } + + @Test + public void testGenericTagDecoder() throws NostrException { + System.out.println("testGenericTagDecoder"); + final String jsonString = "[\"saturn\", \"jetpack\", false]"; + + var tag = new GenericTagDecoder(jsonString).decode(); + + Assertions.assertEquals("saturn", tag.getCode()); + Assertions.assertEquals(2, tag.getAttributes().size()); + Assertions.assertEquals("jetpack", ((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue()); + Assertions.assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString())); + } + + @Test + public void testDeserializeTag() throws NostrException { + System.out.println("testDeserializeTag"); + + String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); + final String jsonString = "[\"p\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; + var tag = new BaseTagDecoder(jsonString).decode(); + + Assertions.assertTrue(tag instanceof PubKeyTag); + + PubKeyTag pTag = (PubKeyTag) tag; + Assertions.assertEquals("wss://nostr.java", pTag.getMainRelayUrl()); + Assertions.assertEquals(npubHex, pTag.getPublicKey().toString()); + Assertions.assertEquals("alice", pTag.getPetName()); + } + + @Test + public void testDeserializeGenericTag() throws NostrException { + System.out.println("testDeserializeGenericTag"); + + String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); + final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; + var tag = new BaseTagDecoder(jsonString).decode(); + + Assertions.assertTrue(tag instanceof GenericTag); + + GenericTag gTag = (GenericTag) tag; + Assertions.assertEquals("gt", gTag.getCode()); + } +} diff --git a/nostr-java-test/src/test/resources/app.properties b/nostr-java-test/src/test/resources/app.properties new file mode 100644 index 000000000..4ff8e632d --- /dev/null +++ b/nostr-java-test/src/test/resources/app.properties @@ -0,0 +1,3 @@ +profile=profile.properties +client=client.properties +relays=relays.properties \ No newline at end of file diff --git a/nostr-test/src/test/resources/profile.properties b/nostr-java-test/src/test/resources/profile.properties similarity index 87% rename from nostr-test/src/test/resources/profile.properties rename to nostr-java-test/src/test/resources/profile.properties index 976a5d158..44f2c68c9 100644 --- a/nostr-test/src/test/resources/profile.properties +++ b/nostr-java-test/src/test/resources/profile.properties @@ -1,3 +1,3 @@ privateKey=519672a628310117110bfa93798eb03566f99b47d75b3998a1d65366aff97988 -publickKey= \ No newline at end of file +publicKey= \ No newline at end of file diff --git a/nostr-util/nb-configuration.xml b/nostr-java-util/nb-configuration.xml similarity index 100% rename from nostr-util/nb-configuration.xml rename to nostr-java-util/nb-configuration.xml diff --git a/nostr-util/pom.xml b/nostr-java-util/pom.xml similarity index 90% rename from nostr-util/pom.xml rename to nostr-java-util/pom.xml index f94355a47..baf587a2e 100644 --- a/nostr-util/pom.xml +++ b/nostr-java-util/pom.xml @@ -6,10 +6,10 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-util + nostr-java-util jar diff --git a/nostr-util/src/main/java/module-info.java b/nostr-java-util/src/main/java/module-info.java similarity index 91% rename from nostr-util/src/main/java/module-info.java rename to nostr-java-util/src/main/java/module-info.java index 9ea7b537f..c7dcf4d46 100644 --- a/nostr-util/src/main/java/module-info.java +++ b/nostr-java-util/src/main/java/module-info.java @@ -5,6 +5,7 @@ module nostr.util { requires static lombok; + requires java.logging; exports nostr.util; } diff --git a/nostr-base/src/main/java/nostr/base/BaseConfiguration.java b/nostr-java-util/src/main/java/nostr/util/AbstractBaseConfiguration.java similarity index 71% rename from nostr-base/src/main/java/nostr/base/BaseConfiguration.java rename to nostr-java-util/src/main/java/nostr/util/AbstractBaseConfiguration.java index 19f27b2ee..20163aeaf 100644 --- a/nostr-base/src/main/java/nostr/base/BaseConfiguration.java +++ b/nostr-java-util/src/main/java/nostr/util/AbstractBaseConfiguration.java @@ -1,4 +1,4 @@ -package nostr.base; +package nostr.util; import java.io.File; import java.io.FileInputStream; @@ -9,7 +9,6 @@ import java.util.logging.Level; import lombok.Data; -import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.extern.java.Log; @@ -19,16 +18,20 @@ */ @Data @Log -@NoArgsConstructor -public class BaseConfiguration { +public abstract class AbstractBaseConfiguration { protected final Properties properties = new Properties(); + protected final ApplicationConfiguration appConfig; private static final String PREFIX_FILE = "file."; private static final String CONFIG_DIR = "config.folder"; - protected BaseConfiguration(@NonNull String file) throws IOException { - load(file); + protected AbstractBaseConfiguration() throws IOException { + this.appConfig = new ApplicationConfiguration(); + } + + protected AbstractBaseConfiguration(ApplicationConfiguration appConfig) { + this.appConfig = appConfig; } protected String getFileLocation(String key) throws FileNotFoundException { @@ -79,13 +82,16 @@ private String getPrefix(String key) { return null; } - private void load(@NonNull String filename) throws FileNotFoundException, IOException { + protected final void load(@NonNull String filename) throws FileNotFoundException, IOException { - var configFolder = System.getProperty(CONFIG_DIR); - - log.log(Level.FINER, "Configuration location: {0}", configFolder); + //var configFolder = System.getProperty(CONFIG_DIR); + var configFolder = this.appConfig.getDefaultConfigFolder(); + log.log(Level.INFO, "loading configuration file: {0}", filename); + log.log(Level.INFO, "Configuration folder location: {0}", configFolder); if (configFolder != null) { - loadFromConfigDir(filename, configFolder); + final var baseConfigFolder = appConfig.getDefaultBaseConfigFolder(); + final var configLocationFolder = new File(baseConfigFolder, configFolder); + loadFromConfigDir(filename, configLocationFolder); return; } @@ -117,16 +123,18 @@ private void loadFromResourceStream(String filename) throws IOException { } } - private void loadFromConfigDir(String filename, String configFolder) throws IOException { + private void loadFromConfigDir(String filename, File configFolder) throws IOException { log.log(Level.FINER, "loadFromConfigDir({0}, {1})", new Object[]{filename, configFolder}); final String fname = filename.substring(1); var tmpFile = filename.startsWith("/") ? fname : filename; - final File file = new File(new File(configFolder), tmpFile); - log.log(Level.FINER, "Configuration file {0}", file.getName()); + final File file = new File(configFolder, tmpFile); + log.log(Level.INFO, "Configuration file {0}", file.getAbsoluteFile()); if (file.exists()) { var inputStream = new FileInputStream(file); - log.log(Level.FINER, "Loading configuration file from {0}", file.getParent()); + log.log(Level.INFO, "Loading configuration file from {0}", file.getParent()); properties.load(inputStream); + } else { + log.log(Level.WARNING, "The file {0} does not exist", file.getAbsoluteFile()); } } diff --git a/nostr-java-util/src/main/java/nostr/util/ApplicationConfiguration.java b/nostr-java-util/src/main/java/nostr/util/ApplicationConfiguration.java new file mode 100644 index 000000000..0da73f124 --- /dev/null +++ b/nostr-java-util/src/main/java/nostr/util/ApplicationConfiguration.java @@ -0,0 +1,80 @@ +package nostr.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.Properties; +import lombok.extern.java.Log; + +/** + * + * @author eric + */ +@Log +public class ApplicationConfiguration { + + private final String DEFAULT_APP_CONFIG = "config"; + private final String DEFAULT_ID_CONFIG = "/profile.properties"; + private final String DEFAULT_RELAYS_CONFIG = "/relays.properties"; + //private final String DEFAULT_CLIENT_CONFIG = "/client.properties"; + private final String DEFAULT_CONFIG_FOLDER = ".nostr-java"; + + private final Properties properties; + + public ApplicationConfiguration() throws IOException { + InputStream appConfig = getAppConfig(); + this.properties = new Properties(); + + log.log(Level.INFO, "Loading the application configuration file..."); + this.properties.load(appConfig); + } + + public String getIdentityProperties() { + var id = getProperty("profile"); + return id == null ? DEFAULT_ID_CONFIG : id; + } + + public String getRelaysProperties() { + var client = getProperty("relays"); + return client == null ? DEFAULT_RELAYS_CONFIG : client; + } + + public String getDefaultConfigFolder() { + return getProperty("config.folder"); + } + + public String getDefaultBaseConfigFolder() { + var dcf = getProperty("config.base"); + return dcf == null ? System.getProperty("user.home") : dcf; + } + + private InputStream getAppConfig() throws FileNotFoundException { + var configFile = System.getProperty(DEFAULT_APP_CONFIG); + return configFile == null ? getAppProperties() : new FileInputStream(configFile); + } + + protected String getProperty(String key) { + return this.properties.getProperty(key); + } + + private InputStream getAppProperties() throws FileNotFoundException { + + if (new File("app.properties").exists()) { + return new FileInputStream("app.properties"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + InputStream resource = classLoader.getResourceAsStream("/app.properties"); + resource = resource == null ? classLoader.getResourceAsStream("app.properties") : resource; + + if (resource == null) { + throw new IllegalStateException("app.properties file not found in resources folder."); + } + + return resource; + } + +} diff --git a/nostr-util/src/main/java/nostr/util/NostrException.java b/nostr-java-util/src/main/java/nostr/util/NostrException.java similarity index 100% rename from nostr-util/src/main/java/nostr/util/NostrException.java rename to nostr-java-util/src/main/java/nostr/util/NostrException.java diff --git a/nostr-util/src/main/java/nostr/util/NostrUtil.java b/nostr-java-util/src/main/java/nostr/util/NostrUtil.java similarity index 100% rename from nostr-util/src/main/java/nostr/util/NostrUtil.java rename to nostr-java-util/src/main/java/nostr/util/NostrUtil.java diff --git a/nostr-util/src/main/java/nostr/util/UnsupportedNIPException.java b/nostr-java-util/src/main/java/nostr/util/UnsupportedNIPException.java similarity index 100% rename from nostr-util/src/main/java/nostr/util/UnsupportedNIPException.java rename to nostr-java-util/src/main/java/nostr/util/UnsupportedNIPException.java diff --git a/nostr-ws-response-handler-provider/pom.xml b/nostr-java-ws-handler-default-command/pom.xml similarity index 64% rename from nostr-ws-response-handler-provider/pom.xml rename to nostr-java-ws-handler-default-command/pom.xml index 6427c7beb..faf3f3b3a 100644 --- a/nostr-ws-response-handler-provider/pom.xml +++ b/nostr-java-ws-handler-default-command/pom.xml @@ -1,25 +1,35 @@ 4.0.0 + nostr-java nostr-java - 0.1 + 0.2 - nostr-ws-response-handler-provider + + nostr-java-ws-handler-command-provider jar + ${project.groupId} - nostr-ws-handler-interface + nostr-java-ws-handler-interface ${project.version} ${project.groupId} - nostr-ws-handler-command-provider + nostr-java-id + ${project.version} + + + ${project.groupId} + nostr-java-client ${project.version} + + \ No newline at end of file diff --git a/nostr-ws-handler-default-command/src/main/java/module-info.java b/nostr-java-ws-handler-default-command/src/main/java/module-info.java similarity index 90% rename from nostr-ws-handler-default-command/src/main/java/module-info.java rename to nostr-java-ws-handler-default-command/src/main/java/module-info.java index 542ab7b2c..e9b44a23c 100644 --- a/nostr-ws-handler-default-command/src/main/java/module-info.java +++ b/nostr-java-ws-handler-default-command/src/main/java/module-info.java @@ -7,6 +7,9 @@ requires nostr.ws.handler; requires nostr.util; requires nostr.base; + requires nostr.id; + requires nostr.event; + requires nostr.client; requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.core; diff --git a/nostr-ws-handler-default-command/src/main/java/nostr/ws/handler/command/provider/DefaultCommandHandler.java b/nostr-java-ws-handler-default-command/src/main/java/nostr/ws/handler/command/provider/DefaultCommandHandler.java similarity index 78% rename from nostr-ws-handler-default-command/src/main/java/nostr/ws/handler/command/provider/DefaultCommandHandler.java rename to nostr-java-ws-handler-default-command/src/main/java/nostr/ws/handler/command/provider/DefaultCommandHandler.java index ac20df8b9..41414ef20 100644 --- a/nostr-ws-handler-default-command/src/main/java/nostr/ws/handler/command/provider/DefaultCommandHandler.java +++ b/nostr-java-ws-handler-default-command/src/main/java/nostr/ws/handler/command/provider/DefaultCommandHandler.java @@ -9,6 +9,9 @@ import nostr.base.Command; import nostr.base.Relay; import nostr.base.annotation.DefaultHandler; +import nostr.client.Client; +import nostr.id.Identity; +import nostr.util.NostrException; import nostr.ws.handler.command.spi.ICommandHandler; import nostr.ws.handler.command.spi.ICommandHandler.Reason; @@ -41,7 +44,16 @@ public void onEvent(String jsonEvent, String subId, Relay relay) { } @Override - public void onAuth(String challenge, Relay relay) { + public void onAuth(String challenge, Relay relay) throws NostrException { log.log(Level.INFO, "Command: {0} - Challenge: {1} - Relay {3}", new Object[]{Command.AUTH, challenge, relay}); + + var client = Client.getInstance(); + var identity = Identity.getInstance(); + + client.auth(identity, challenge, relay); + } + + public static void auth(String challenge, Relay relay) throws NostrException { + new DefaultCommandHandler().onAuth(challenge, relay); } } diff --git a/nostr-ws-handler-default-command/src/main/resources/META-INF/services/nostr.ws.handler.command.spi.ICommandHandler b/nostr-java-ws-handler-default-command/src/main/resources/META-INF/services/nostr.ws.handler.command.spi.ICommandHandler similarity index 100% rename from nostr-ws-handler-default-command/src/main/resources/META-INF/services/nostr.ws.handler.command.spi.ICommandHandler rename to nostr-java-ws-handler-default-command/src/main/resources/META-INF/services/nostr.ws.handler.command.spi.ICommandHandler diff --git a/nostr-ws-handler/pom.xml b/nostr-java-ws-handler-interface/pom.xml similarity index 77% rename from nostr-ws-handler/pom.xml rename to nostr-java-ws-handler-interface/pom.xml index 36af202d2..f8ae36f27 100644 --- a/nostr-ws-handler/pom.xml +++ b/nostr-java-ws-handler-interface/pom.xml @@ -5,25 +5,25 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-ws-handler-interface + nostr-java-ws-handler-interface jar nostr-java - nostr-util + nostr-java-util ${project.version} nostr-java - nostr-base + nostr-java-base ${project.version} ${project.groupId} - nostr-event + nostr-java-event ${project.version} diff --git a/nostr-ws-handler/src/main/java/module-info.java b/nostr-java-ws-handler-interface/src/main/java/module-info.java similarity index 100% rename from nostr-ws-handler/src/main/java/module-info.java rename to nostr-java-ws-handler-interface/src/main/java/module-info.java diff --git a/nostr-ws-handler/src/main/java/nostr/ws/handler/command/spi/ICommandHandler.java b/nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/command/spi/ICommandHandler.java similarity index 92% rename from nostr-ws-handler/src/main/java/nostr/ws/handler/command/spi/ICommandHandler.java rename to nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/command/spi/ICommandHandler.java index ba1d81d0e..ffaa2113a 100644 --- a/nostr-ws-handler/src/main/java/nostr/ws/handler/command/spi/ICommandHandler.java +++ b/nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/command/spi/ICommandHandler.java @@ -8,6 +8,7 @@ import java.util.Optional; import nostr.base.IHandler; import nostr.base.Relay; +import nostr.util.NostrException; /** * @@ -23,7 +24,7 @@ public interface ICommandHandler extends IHandler { public abstract void onEvent(String jsonEvent, String subId, Relay relay); - public abstract void onAuth(String challenge, Relay relay); + public abstract void onAuth(String challenge, Relay relay) throws NostrException; public enum Reason { UNDEFINED(""), diff --git a/nostr-ws-handler/src/main/java/nostr/ws/handler/spi/IRequestHandler.java b/nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/spi/IRequestHandler.java similarity index 60% rename from nostr-ws-handler/src/main/java/nostr/ws/handler/spi/IRequestHandler.java rename to nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/spi/IRequestHandler.java index e0b92c30f..b75aa3fb7 100644 --- a/nostr-ws-handler/src/main/java/nostr/ws/handler/spi/IRequestHandler.java +++ b/nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/spi/IRequestHandler.java @@ -2,7 +2,7 @@ import nostr.base.IHandler; import nostr.base.Relay; -import nostr.event.impl.GenericMessage; +import nostr.event.BaseMessage; import nostr.util.NostrException; /** @@ -11,5 +11,5 @@ */ public interface IRequestHandler extends IHandler { - public abstract void process(GenericMessage message, Relay relay) throws NostrException; + public abstract void process(BaseMessage message, Relay relay) throws NostrException; } diff --git a/nostr-ws-handler/src/main/java/nostr/ws/handler/spi/IResponseHandler.java b/nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/spi/IResponseHandler.java similarity index 100% rename from nostr-ws-handler/src/main/java/nostr/ws/handler/spi/IResponseHandler.java rename to nostr-java-ws-handler-interface/src/main/java/nostr/ws/handler/spi/IResponseHandler.java diff --git a/nostr-ws-request-handler-provider/pom.xml b/nostr-java-ws-request-handler-provider/pom.xml similarity index 78% rename from nostr-ws-request-handler-provider/pom.xml rename to nostr-java-ws-request-handler-provider/pom.xml index 5f75d809d..64f10def3 100644 --- a/nostr-ws-request-handler-provider/pom.xml +++ b/nostr-java-ws-request-handler-provider/pom.xml @@ -4,19 +4,19 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-ws-request-handler-provider + nostr-java-ws-request-handler-provider jar ${project.groupId} - nostr-ws + nostr-java-ws ${project.version} ${project.groupId} - nostr-ws-handler-interface + nostr-java-ws-handler-interface ${project.version} diff --git a/nostr-ws-request-handler-provider/src/main/java/module-info.java b/nostr-java-ws-request-handler-provider/src/main/java/module-info.java similarity index 100% rename from nostr-ws-request-handler-provider/src/main/java/module-info.java rename to nostr-java-ws-request-handler-provider/src/main/java/module-info.java diff --git a/nostr-ws-request-handler-provider/src/main/java/nostr/ws/request/handler/provider/DefaultRequestHandler.java b/nostr-java-ws-request-handler-provider/src/main/java/nostr/ws/request/handler/provider/DefaultRequestHandler.java similarity index 81% rename from nostr-ws-request-handler-provider/src/main/java/nostr/ws/request/handler/provider/DefaultRequestHandler.java rename to nostr-java-ws-request-handler-provider/src/main/java/nostr/ws/request/handler/provider/DefaultRequestHandler.java index 882b01f61..d52ab8cbd 100644 --- a/nostr-ws-request-handler-provider/src/main/java/nostr/ws/request/handler/provider/DefaultRequestHandler.java +++ b/nostr-java-ws-request-handler-provider/src/main/java/nostr/ws/request/handler/provider/DefaultRequestHandler.java @@ -11,8 +11,9 @@ import lombok.extern.java.Log; import nostr.base.Relay; import nostr.base.annotation.DefaultHandler; +import nostr.event.BaseMessage; import nostr.event.impl.GenericMessage; -import nostr.event.marshaller.impl.MessageMarshaller; +import nostr.event.json.codec.BaseMessageEncoder; import nostr.util.NostrException; import nostr.util.UnsupportedNIPException; import nostr.ws.Connection; @@ -31,7 +32,7 @@ public class DefaultRequestHandler implements IRequestHandler { private Connection connection; @Override - public void process(GenericMessage message, Relay relay) throws NostrException { + public void process(BaseMessage message, Relay relay) throws NostrException { try { this.connection = new Connection(relay); sendMessage(message); @@ -46,7 +47,7 @@ public void process(GenericMessage message, Relay relay) throws NostrException { } } - private void sendMessage(GenericMessage message) throws IOException, NostrException { + private void sendMessage(BaseMessage message) throws IOException, NostrException { final Relay relay = connection.getRelay(); @@ -58,7 +59,7 @@ private void sendMessage(GenericMessage message) throws IOException, NostrExcept if (session != null) { RemoteEndpoint remote = session.getRemote(); - final String msg = new MessageMarshaller(message, relay).marshall(); + final String msg = new BaseMessageEncoder(message, relay).encode(); log.log(Level.INFO, ">>> Sending Message: {0}", msg); diff --git a/nostr-ws-request-handler-provider/src/main/resources/META-INF/services/nostr.ws.handler.spi.IRequestHandler b/nostr-java-ws-request-handler-provider/src/test/resources/META-INF/services/nostr.ws.handler.spi.IRequestHandler similarity index 100% rename from nostr-ws-request-handler-provider/src/main/resources/META-INF/services/nostr.ws.handler.spi.IRequestHandler rename to nostr-java-ws-request-handler-provider/src/test/resources/META-INF/services/nostr.ws.handler.spi.IRequestHandler diff --git a/nostr-ws-handler-default-command/pom.xml b/nostr-java-ws-response-handler-provider/pom.xml similarity index 78% rename from nostr-ws-handler-default-command/pom.xml rename to nostr-java-ws-response-handler-provider/pom.xml index 59f9090dd..726b46143 100644 --- a/nostr-ws-handler-default-command/pom.xml +++ b/nostr-java-ws-response-handler-provider/pom.xml @@ -1,25 +1,20 @@ 4.0.0 - nostr-java nostr-java - 0.1 + 0.2 - - nostr-ws-handler-command-provider + nostr-java-ws-response-handler-provider jar - ${project.groupId} - nostr-ws-handler-interface + nostr-java-ws-handler-interface ${project.version} - - \ No newline at end of file diff --git a/nostr-ws-response-handler-provider/src/main/java/module-info.java b/nostr-java-ws-response-handler-provider/src/main/java/module-info.java similarity index 96% rename from nostr-ws-response-handler-provider/src/main/java/module-info.java rename to nostr-java-ws-response-handler-provider/src/main/java/module-info.java index 514d27943..3e27b5bcd 100644 --- a/nostr-ws-response-handler-provider/src/main/java/module-info.java +++ b/nostr-java-ws-response-handler-provider/src/main/java/module-info.java @@ -8,6 +8,7 @@ requires nostr.util; requires nostr.base; requires nostr.crypto; + requires nostr.event; requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.annotation; diff --git a/nostr-java-ws-response-handler-provider/src/main/java/nostr/ws/response/handler/provider/ResponseHandlerImpl.java b/nostr-java-ws-response-handler-provider/src/main/java/nostr/ws/response/handler/provider/ResponseHandlerImpl.java new file mode 100644 index 000000000..1ff2d1ba7 --- /dev/null +++ b/nostr-java-ws-response-handler-provider/src/main/java/nostr/ws/response/handler/provider/ResponseHandlerImpl.java @@ -0,0 +1,127 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + */ +package nostr.ws.response.handler.provider; + +import java.util.NoSuchElementException; +import java.util.ServiceLoader; +import java.util.logging.Level; +import lombok.Data; +import lombok.extern.java.Log; +import nostr.base.Relay; +import nostr.base.annotation.DefaultHandler; +import nostr.event.json.codec.BaseMessageDecoder; +import nostr.event.message.ClientAuthenticationMessage; +import nostr.event.message.EoseMessage; +import nostr.event.message.EventMessage; +import nostr.event.message.NoticeMessage; +import nostr.event.message.OkMessage; +import nostr.event.message.RelayAuthenticationMessage; +import nostr.util.NostrException; +import nostr.ws.handler.command.spi.ICommandHandler; +import nostr.ws.handler.command.spi.ICommandHandler.Reason; +import nostr.ws.handler.spi.IResponseHandler; + +/** + * + * @author eric + */ +@Data +@DefaultHandler +@Log +public class ResponseHandlerImpl implements IResponseHandler { + + private ICommandHandler commandHandler; + + public ResponseHandlerImpl() { + + try { + this.commandHandler = ServiceLoader + .load(ICommandHandler.class).stream().map(p -> p.get()) + .filter(ch -> !ch.getClass().isAnnotationPresent(DefaultHandler.class)) + .findFirst() + .get(); + } catch (NoSuchElementException ex) { + log.log(Level.WARNING, "No custom command handler provided. Using default command handler..."); + try { + this.commandHandler = ServiceLoader + .load(ICommandHandler.class).stream().map(p -> p.get()) + .filter(ch -> ch.getClass().isAnnotationPresent(DefaultHandler.class)) + .findFirst() + .get(); + } catch (NoSuchElementException e) { + throw new AssertionError("Could not load the default handler", e); + } + } + } + + @Override + public void process(String message, Relay relay) throws NostrException { + + log.log(Level.INFO, "Process Message: {0} from relay: {1}", new Object[]{message, relay}); + + var oMsg = new BaseMessageDecoder(message).decode(); + final String command = oMsg.getCommand(); + + switch (command) { + case "EOSE" -> { + if (oMsg instanceof EoseMessage msg) { + commandHandler.onEose(msg.getSubscriptionId(), relay); + } else { + throw new AssertionError("EOSE"); + } + } + case "OK" -> { + if (oMsg instanceof OkMessage msg) { + String eventId = msg.getEventId(); + boolean result = msg.getFlag(); + String strMsg = msg.getMessage(); + final var msgSplit = strMsg.split(":", 2); + Reason reason; + String reasonMessage = strMsg; + if (msgSplit.length < 2) { + reason = Reason.UNDEFINED; + } else { + reason = Reason.fromCode(msgSplit[0]).orElseThrow(RuntimeException::new); + reasonMessage = msgSplit[1]; + } + + commandHandler.onOk(eventId, reasonMessage, reason, result, relay); + } else { + throw new AssertionError("OK"); + } + } + case "NOTICE" -> { + if (oMsg instanceof NoticeMessage msg) { + commandHandler.onNotice(msg.getMessage()); + } else { + throw new AssertionError("NOTICE"); + } + } + case "EVENT" -> { + if (oMsg instanceof EventMessage msg) { + var subId = msg.getSubscriptionId(); + var jsonEvent = msg.getEvent().toString(); + commandHandler.onEvent(jsonEvent, subId, relay); + } else { + throw new AssertionError("EVENT"); + } + } + + case "AUTH" -> { + if (oMsg instanceof RelayAuthenticationMessage msg) { + var challenge = msg.getChallenge(); + commandHandler.onAuth(challenge, relay); + } else if (oMsg instanceof ClientAuthenticationMessage msg) { + // Actually, do nothing! + } else { + throw new AssertionError("AUTH"); + } + + } + default -> { + throw new AssertionError("Unknown command " + command); + } + } + } +} diff --git a/nostr-ws-response-handler-provider/src/main/resources/META-INF/services/nostr.ws.handler.spi.IResponseHandler b/nostr-java-ws-response-handler-provider/src/main/resources/META-INF/services/nostr.ws.handler.spi.IResponseHandler similarity index 100% rename from nostr-ws-response-handler-provider/src/main/resources/META-INF/services/nostr.ws.handler.spi.IResponseHandler rename to nostr-java-ws-response-handler-provider/src/main/resources/META-INF/services/nostr.ws.handler.spi.IResponseHandler diff --git a/nostr-ws/nb-configuration.xml b/nostr-java-ws/nb-configuration.xml similarity index 100% rename from nostr-ws/nb-configuration.xml rename to nostr-java-ws/nb-configuration.xml diff --git a/nostr-ws/pom.xml b/nostr-java-ws/pom.xml similarity index 85% rename from nostr-ws/pom.xml rename to nostr-java-ws/pom.xml index 3e6ac286a..b70c834e6 100644 --- a/nostr-ws/pom.xml +++ b/nostr-java-ws/pom.xml @@ -6,10 +6,10 @@ nostr-java nostr-java - 0.1 + 0.2 - nostr-ws + nostr-java-ws jar @@ -37,22 +37,22 @@ ${project.groupId} - nostr-event + nostr-java-event ${project.version} ${project.groupId} - nostr-util + nostr-java-util ${project.version} ${project.groupId} - nostr-ws-handler-interface + nostr-java-ws-handler-interface ${project.version} ${project.groupId} - nostr-ws-response-handler-provider + nostr-java-ws-response-handler-provider ${project.version} diff --git a/nostr-ws/src/main/java/module-info.java b/nostr-java-ws/src/main/java/module-info.java similarity index 100% rename from nostr-ws/src/main/java/module-info.java rename to nostr-java-ws/src/main/java/module-info.java diff --git a/nostr-ws/src/main/java/nostr/ws/ClientListenerEndPoint.java b/nostr-java-ws/src/main/java/nostr/ws/ClientListenerEndPoint.java similarity index 83% rename from nostr-ws/src/main/java/nostr/ws/ClientListenerEndPoint.java rename to nostr-java-ws/src/main/java/nostr/ws/ClientListenerEndPoint.java index 23e7ff793..3409fe21d 100644 --- a/nostr-ws/src/main/java/nostr/ws/ClientListenerEndPoint.java +++ b/nostr-java-ws/src/main/java/nostr/ws/ClientListenerEndPoint.java @@ -1,12 +1,8 @@ package nostr.ws; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.Arrays; -import java.util.List; import java.util.logging.Level; import lombok.NonNull; @@ -71,9 +67,7 @@ public void onTextMessage(Session session, @NonNull String message) throws IOExc session.close(StatusCode.NORMAL, "bye"); return; } - - log.log(Level.FINE, "onTextMessage Relay {0}: Message: {1}", new Object[]{session.getRemoteAddress(), message}); - + responseHandler.process(message, getRelay(session)); } @@ -102,15 +96,6 @@ private void disposeResources() { log.log(Level.FINE, "disposeResources"); } - private List unmarshall(String message) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - return Arrays.asList(objectMapper.readValue(message, String[].class)); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } - } - private void savePNGImage(byte[] payload, int offset, int length) { log.log(Level.FINE, "savePNGImage"); } diff --git a/nostr-ws/src/main/java/nostr/ws/Connection.java b/nostr-java-ws/src/main/java/nostr/ws/Connection.java similarity index 96% rename from nostr-ws/src/main/java/nostr/ws/Connection.java rename to nostr-java-ws/src/main/java/nostr/ws/Connection.java index c82a07471..0f205ccbd 100644 --- a/nostr-ws/src/main/java/nostr/ws/Connection.java +++ b/nostr-java-ws/src/main/java/nostr/ws/Connection.java @@ -183,7 +183,7 @@ public String getRelayInformation() throws InterruptedException, TimeoutExceptio public void updateRelayMetadata() throws Exception { String strInfo = getRelayInformation(); - log.log(Level.FINE, "Relay information: {0}", strInfo); + log.log(Level.INFO, "Relay information: {0}", strInfo); ObjectMapper objectMapper = new ObjectMapper(); var relayInfoDoc = objectMapper.readValue(strInfo, Relay.RelayInformationDocument.class); diff --git a/nostr-ws/src/main/resources/logging.properties b/nostr-java-ws/src/main/resources/logging.properties similarity index 100% rename from nostr-ws/src/main/resources/logging.properties rename to nostr-java-ws/src/main/resources/logging.properties diff --git a/nostr-test/src/main/java/nostr/test/EntityFactory.java b/nostr-test/src/main/java/nostr/test/EntityFactory.java deleted file mode 100644 index 265bbfc9a..000000000 --- a/nostr-test/src/main/java/nostr/test/EntityFactory.java +++ /dev/null @@ -1,265 +0,0 @@ -package nostr.test; - -import java.beans.IntrospectionException; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import lombok.extern.java.Log; -import nostr.base.ElementAttribute; -import nostr.base.GenericTagQuery; -import nostr.base.IEvent; -import nostr.base.Profile; -import nostr.base.PublicKey; -import nostr.event.Reaction; -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; -import nostr.event.impl.MentionsEvent; -import nostr.event.impl.MetadataEvent; -import nostr.event.impl.OtsEvent; -import nostr.event.impl.ReactionEvent; -import nostr.event.impl.ReplaceableEvent; -import nostr.event.impl.TextNoteEvent; -import nostr.event.list.EventList; -import nostr.event.list.GenericTagQueryList; -import nostr.event.list.KindList; -import nostr.event.list.PubKeyTagList; -import nostr.event.list.PublicKeyList; -import nostr.event.list.TagList; -import nostr.event.tag.PubKeyTag; -import nostr.util.NostrException; - -/** - * - * @author squirrel - */ -@Log -//TODO - Add the sender PK to all createEvents. -public class EntityFactory { - - @Log - public static class Events { - - @SuppressWarnings("unchecked") - public static EphemeralEvent createEphemeralEvent(PublicKey publicKey) { - try { - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("eric").build()); - GenericEvent event = new EphemeralEvent(publicKey, tagList); - event.update(); - return (EphemeralEvent) event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - public static DirectMessageEvent createDirectMessageEvent(PublicKey senderPublicKey,PublicKey rcptPublicKey, String content) { - try { - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(rcptPublicKey).petName("uq7yfx3l").build()); - GenericEvent event = new DirectMessageEvent(senderPublicKey, tagList, content); - event.update(); - return (DirectMessageEvent) event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - public static Filters createFilters(PublicKeyList authors, KindList kindList, Long since) { - return Filters.builder().authors(authors).kinds(kindList).since(since).build(); - } - - @SuppressWarnings("unchecked") - public static InternetIdentifierMetadataEvent createInternetIdentifierMetadataEvent(Profile profile) { - try { - final PublicKey publicKey = profile.getPublicKey(); - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("daniel").build()); - GenericEvent event = new InternetIdentifierMetadataEvent(publicKey, tagList, profile); - event.update(); - return (InternetIdentifierMetadataEvent) event; - } catch (NostrException | NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException ex) { - throw new RuntimeException(ex); - } - } - - @SuppressWarnings("unchecked") - public static MentionsEvent createMentionsEvent(PublicKey publicKey, PubKeyTagList mentionees) { - try { - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("charlie").build()); - String content = generateRamdomAlpha(32); - StringBuilder sbContent = new StringBuilder(content); - - int len = mentionees.size(); - for (int i = 0; i < len; i++) { - sbContent.append(", ").append(((PubKeyTag) mentionees.getList().get(i)).getPublicKey().toString()); - - } - GenericEvent event = new MentionsEvent(publicKey, tagList, sbContent.toString(), mentionees); - event.update(); - return (MentionsEvent) event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - @SuppressWarnings("unchecked") - public static MetadataEvent createMetadataEvent(Profile profile) { - try { - final PublicKey publicKey = profile.getPublicKey(); - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("charlie").build()); - GenericEvent event = new MetadataEvent(publicKey, tagList, profile); - event.update(); - return (MetadataEvent) event; - } catch (NostrException | NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException ex) { - throw new RuntimeException(ex); - } - } - - @SuppressWarnings("unchecked") - public static ReactionEvent createReactionEvent(PublicKey publicKey, GenericEvent original) { - try { - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("charlie").build()); - GenericEvent event = new ReactionEvent(publicKey, tagList, Reaction.LIKE, original); - event.update(); - return (ReactionEvent) event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - public static ReplaceableEvent createReplaceableEvent(GenericEvent original) { - try { - String content = generateRamdomAlpha(32); - TagList tagList = original.getTags(); - PublicKey publicKey = original.getPubKey(); - GenericEvent event = new ReplaceableEvent(publicKey, tagList, content, original); - event.update(); - return (ReplaceableEvent) event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - public static TextNoteEvent createTextNoteEvent(PublicKey publicKey) { - String content = generateRamdomAlpha(32); - return createTextNoteEvent(publicKey, content); - } - - @SuppressWarnings("unchecked") - public static TextNoteEvent createTextNoteEvent(PublicKey publicKey, String content) { - try { - TagList tagList = new TagList(); - tagList.add(PubKeyTag.builder().publicKey(publicKey).petName("alice").build()); - GenericEvent event = new TextNoteEvent(publicKey, tagList, content); - event.update(); - return (TextNoteEvent) event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - public static OtsEvent createOtsEvent(PublicKey publicKey) { - try { - TagList tagList = new TagList(); - final PubKeyTag pkTag = PubKeyTag.builder().publicKey(publicKey).petName("bob").build(); - tagList.add(pkTag); - OtsEvent event = new OtsEvent(publicKey, tagList, generateRamdomAlpha(32), generateRamdomAlpha(32)); - event.update(); - return event; - } catch (NoSuchAlgorithmException | IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException | NostrException ex) { - throw new RuntimeException(ex); - } - } - - public static GenericTag createGenericTag(PublicKey publicKey) { - IEvent event = createTextNoteEvent(publicKey); - return createGenericTag(publicKey, event); - } - - public static GenericTag createGenericTag(PublicKey publicKey, IEvent event) { - GenericTag tag = new GenericTag("devil"); - tag.addAttribute(ElementAttribute.builder().value("Lucifer").nip(666).build()); - ((GenericEvent) event).addTag(tag); - return tag; - } - - public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Integer tagNip) { - GenericTag tag = new GenericTag(tagNip, "devil"); - tag.addAttribute(ElementAttribute.builder().value("Lucifer").nip(666).build()); - ((GenericEvent) event).addTag(tag); - return tag; - } - - public static Filters createFilters(PublicKey publicKey) { - EventList eventList = new EventList(); - eventList.add(createTextNoteEvent(publicKey)); - eventList.add(createEphemeralEvent(publicKey)); - - EventList refEvents = new EventList(); - refEvents.add(createTextNoteEvent(publicKey)); - - GenericTagQueryList gtqList = new GenericTagQueryList(); - gtqList.add(createGenericTagQuery()); - - return Filters.builder().events(eventList).referencedEvents(refEvents).genericTagQueryList(gtqList).build(); - } - - public static GenericTagQuery createGenericTagQuery() { - Character c = generateRamdomAlpha(1).charAt(0); - String v1 = generateRamdomAlpha(5); - String v2 = generateRamdomAlpha(6); - String v3 = generateRamdomAlpha(7); - - List list = new ArrayList<>(); - list.add(v3); - list.add(v2); - list.add(v1); - return GenericTagQuery.builder().tagName(c).value(list).build(); - } - } - - public static Profile createProfile(PublicKey pubKey) { - try { - String number = EntityFactory.generateRandomNumber(4); - String about = "about_" + number; - String name = "name_" + number; - String nip05 = name + "@tcheeric.com"; - String url = "http://assets.tcheeric.com/" + number + ".PNG"; - - return Profile.builder().about(about).name(name).nip05(nip05).picture(new URL(url)).publicKey(pubKey).build(); - - } catch (MalformedURLException ex) { - throw new RuntimeException(ex); - } - } - - public static String generateRamdomAlpha(int len) { - return generateRandom(58, 122, len); - } - - public static String generateRandomNumber(int len) { - return generateRandom(48, 57, len); - } - - private static String generateRandom(int leftLimit, int rightLimit, int len) { - - return new Random().ints(leftLimit, rightLimit + 1) - .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) - .limit(len) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) - .toString(); - } - -} diff --git a/nostr-ws-response-handler-provider/src/main/java/nostr/ws/response/handler/provider/ResponseHandlerImpl.java b/nostr-ws-response-handler-provider/src/main/java/nostr/ws/response/handler/provider/ResponseHandlerImpl.java deleted file mode 100644 index 4d009b2ea..000000000 --- a/nostr-ws-response-handler-provider/src/main/java/nostr/ws/response/handler/provider/ResponseHandlerImpl.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - */ -package nostr.ws.response.handler.provider; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Arrays; -import java.util.List; -import java.util.ServiceLoader; -import java.util.logging.Level; -import lombok.Data; -import lombok.extern.java.Log; -import nostr.base.Relay; -import nostr.base.annotation.DefaultHandler; -import nostr.util.NostrException; -import nostr.ws.handler.command.spi.ICommandHandler; -import nostr.ws.handler.command.spi.ICommandHandler.Reason; -import nostr.ws.handler.spi.IResponseHandler; - -/** - * - * @author eric - */ -@Data -@DefaultHandler -@Log -public class ResponseHandlerImpl implements IResponseHandler { - - private final ICommandHandler commandHandler; - - public ResponseHandlerImpl() { - - this.commandHandler = ServiceLoader - .load(ICommandHandler.class).stream().map(p -> p.get()) - .filter(ch -> !ch.getClass().isAnnotationPresent(DefaultHandler.class)) - .findFirst() - .get(); - } - - @Override - public void process(String message, Relay relay) throws NostrException { - - log.log(Level.INFO, "Process Message: {0} from relay: {1}", new Object[]{message, relay}); - - ObjectMapper objectMapper = new ObjectMapper(); - List items; - try { - items = Arrays.asList(objectMapper.readValue(message, String[].class)); - } catch (JsonProcessingException ex) { - throw new NostrException(ex); - } - - final String command = items.get(0); - - switch (command) { - case "EOSE" -> { - var subId = items.get(1); - commandHandler.onEose(subId, relay); - } - case "OK" -> { - String eventId = items.get(1); - boolean result = Boolean.parseBoolean(items.get(2)); - String msg = items.get(3); - final var msgSplit = msg.split(":", 2); - Reason reason; - String reasonMessage = msg; - if (msgSplit.length < 2) { - reason = Reason.UNDEFINED; - } else { - reason = Reason.fromCode(msgSplit[0]).orElseThrow(RuntimeException::new); - reasonMessage = msgSplit[1]; - } - - commandHandler.onOk(eventId, reasonMessage, reason, result, relay); - } - case "NOTICE" -> { - var param = items.get(1); - commandHandler.onNotice(param); - } - case "EVENT" -> { - var subId = items.get(1); - var jsonEvent = items.get(2); - - commandHandler.onEvent(jsonEvent, subId, relay); - } - case "AUTH" -> { - var challenge = items.get(1); - - commandHandler.onAuth(challenge, relay); - } - default -> { - throw new AssertionError(); - } - } - } -} diff --git a/pom.xml b/pom.xml index b0288e8c4..d8e45d621 100644 --- a/pom.xml +++ b/pom.xml @@ -5,22 +5,23 @@ nostr-java nostr-java - 0.1 + 0.2 pom - nostr-event - nostr-base - nostr-id - nostr-test - nostr-examples - nostr-util - nostr-crypto - nostr-ws - nostr-ws-handler - nostr-ws-response-handler-provider - nostr-ws-handler-default-command - nostr-ws-request-handler-provider + nostr-java-base + nostr-java-crypto + nostr-java-event + nostr-java-examples + nostr-java-id + nostr-java-test + nostr-java-util + nostr-java-ws + nostr-java-ws-handler-interface + nostr-java-ws-handler-default-command + nostr-java-ws-request-handler-provider + nostr-java-ws-response-handler-provider + nostr-java-client @@ -56,6 +57,11 @@ jackson-databind 2.14.1 + + org.bouncycastle + bcprov-jdk15on + 1.70 +