diff --git a/nostr-java-api/src/main/java/nostr/api/NIP01.java b/nostr-java-api/src/main/java/nostr/api/NIP01.java index bf3f22e13..5b66efc20 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP01.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP01.java @@ -82,13 +82,9 @@ public NIP01 createTextNoteEvent(@NonNull Identity sender, @NonNull String co * @param content the content of the note * @return a text note event */ - public NIP01 createTextNoteEvent(@NonNull List tags, @NonNull String content) { - var sender = getSender(); - var factory = (sender!=null) ? new TextNoteEventFactory(sender, tags, content) : new TextNoteEventFactory(sender, tags, content); - var event = factory.create(); - setEvent((T) event); - - return this; + public NIP01 createTextNoteEvent(@NonNull List tags, @NonNull String content) { + setEvent((T) new TextNoteEventFactory(getSender(), tags, content).create()); + return this; } public NIP01 createMetadataEvent(@NonNull UserProfile profile) { diff --git a/nostr-java-api/src/main/java/nostr/api/NIP57.java b/nostr-java-api/src/main/java/nostr/api/NIP57.java index 5288fecbc..c85f7195a 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP57.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP57.java @@ -1,18 +1,17 @@ -/* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template - */ package nostr.api; import lombok.NonNull; import nostr.api.factory.TagFactory; -import nostr.api.factory.impl.GenericEventFactory; +import nostr.api.factory.impl.NIP57Impl.ZapReceiptEventFactory; +import nostr.api.factory.impl.NIP57Impl.ZapRequestEventFactory; import nostr.base.ElementAttribute; import nostr.base.PublicKey; import nostr.base.Relay; +import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; -import nostr.event.tag.EventTag; +import nostr.event.impl.ZapRequest; +import nostr.event.tag.*; import nostr.id.Identity; import java.util.ArrayList; @@ -22,10 +21,6 @@ * @author eric */ public class NIP57 extends EventNostr { - - - private static final int ZAP_EVENT_KIND = 9734; - private static final int ZAP_RECEIPT_EVENT_KIND = 9735; private static final String LNURL_TAG_NAME = "lnurl"; private static final String BOLT11_TAG_NAME = "bolt11"; private static final String PREIMAGE_TAG_NAME = "preimage"; @@ -37,66 +32,35 @@ public NIP57(@NonNull Identity sender) { setSender(sender); } - /** - * @param content - */ - public NIP57 createZapEvent(String content) { - var factory = new GenericEventFactory(getSender(), ZAP_EVENT_KIND, content); - var event = factory.create(); - setEvent((T) event); - + public NIP57 createZapRequestEvent(@NonNull PublicKey recipientPubKey, @NonNull List baseTags, String content, @NonNull ZapRequest zapRequest) { + setEvent((T) new ZapRequestEventFactory(getSender(), recipientPubKey, baseTags, content, zapRequest).create()); return this; } - /** - * @param amount - * @param lnurl - * @param relay - * @param recipient - * @param eventTag - * @param content - */ - public NIP57 createZapEvent(@NonNull Integer amount, @NonNull String lnurl, Relay relay, @NonNull PublicKey recipient, EventTag eventTag, String content) { - var factory = new GenericEventFactory(getSender(), ZAP_EVENT_KIND, content); - var event = factory.create(); - setEvent((T) event); + public NIP57 createZapRequestEvent(@NonNull PublicKey recipientPubKey, @NonNull List baseTags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull RelaysTag relaysTags) { + return createZapRequestEvent(recipientPubKey, baseTags, content, new ZapRequest(relaysTags, amount, lnUrl)); + } - return this.addAmountTag(amount) - .addLnurlTag(lnurl) - .addRelayTag(relay) - .addRecipientTag(recipient) - .addEventTag(eventTag); + public NIP57 createZapRequestEventFromList(@NonNull PublicKey recipientPubKey, @NonNull List baseTags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull List relays) { + return createZapRequestEvent(recipientPubKey, baseTags, content, amount, lnUrl, new RelaysTag(relays)); } - /** - * - */ - public NIP57 createZapReceiptEvent() { - var factory = new GenericEventFactory(getSender(), ZAP_RECEIPT_EVENT_KIND, ""); - var event = factory.create(); - setEvent((T) event); + public NIP57 createZapRequestEvent(@NonNull PublicKey recipientPubKey, @NonNull List baseTags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull List relays) { + return createZapRequestEventFromList(recipientPubKey, baseTags, content, amount, lnUrl, relays.stream().map(Relay::new).toList()); + } - return this; + public NIP57 createZapRequestEvent(@NonNull PublicKey recipientPubKey, @NonNull List baseTags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull String... relaysTags) { + return createZapRequestEvent(recipientPubKey, baseTags, content, amount, lnUrl, List.of(relaysTags)); } - /** - * @param zapEvent - * @param bolt11 - * @param preimage - * @param recipient - * @param eventTag - * @return - */ - public NIP57 createZapReceiptEvent(@NonNull GenericEvent zapEvent, @NonNull String bolt11, @NonNull String preimage, @NonNull PublicKey recipient, @NonNull EventTag eventTag) { - var factory = new GenericEventFactory(getSender(), ZAP_RECEIPT_EVENT_KIND, ""); - var event = factory.create(); - setEvent((T) event); + public NIP57 createZapReceiptEvent(@NonNull PubKeyTag zapRequestPubKeyTag, List baseTags, EventTag zapRequestEventTag, AddressTag zapRequestAddressTag, @NonNull String bolt11, + @NonNull String descriptionSha256, @NonNull String preimage) { + setEvent((T) new ZapReceiptEventFactory(getSender(), baseTags, zapRequestPubKeyTag, zapRequestEventTag, zapRequestAddressTag, bolt11, descriptionSha256, preimage).create()); + return this; + } - return this.addBolt11Tag(bolt11) - .addDescriptionTag(Nostr.Json.encode(zapEvent)) - .addPreImageTag(preimage) - .addRecipientTag(recipient) - .addEventTag(eventTag); + public NIP57 createZapReceiptEvent(@NonNull String zapRequestPubKeyTag, List baseTags, String zapRequestEventTag, String zapReceiptAddressTag, String zapReceiptIdentifier, String zapReceiptRelayUri, String bolt11, String descriptionSha256, String preimage) { + return createZapReceiptEvent(new PubKeyTag(new PublicKey(zapRequestPubKeyTag)), baseTags, new EventTag(zapRequestEventTag), new AddressTag(null, new PublicKey(zapReceiptAddressTag), new IdentifierTag(zapReceiptIdentifier), new Relay(zapReceiptRelayUri)), bolt11, descriptionSha256, preimage); } public NIP57 addLnurlTag(@NonNull String lnurl) { @@ -134,21 +98,33 @@ public NIP57 addRecipientTag(@NonNull PublicKey recipient) { return this; } - public NIP57 addZapTag(@NonNull PublicKey receiver, @NonNull Relay relay, Integer weight) { - getEvent().addTag(createZapTag(receiver, relay, weight)); + public NIP57 addZapTag(@NonNull PublicKey receiver, @NonNull List relays, Integer weight) { + getEvent().addTag(createZapTag(receiver, relays, weight)); return this; } - public NIP57 addZapTag(@NonNull PublicKey receiver, @NonNull Relay relay) { - getEvent().addTag(createZapTag(receiver, relay)); + public NIP57 addZapTag(@NonNull PublicKey receiver, @NonNull List relays) { + getEvent().addTag(createZapTag(receiver, relays)); return this; } - public NIP57 addRelayTag(@NonNull Relay relay) { - getEvent().addTag(NIP42.createRelayTag(relay)); + public NIP57 addRelaysTag(@NonNull RelaysTag relaysTag) { + getEvent().addTag(relaysTag); return this; } + public NIP57 addRelaysList(@NonNull List relays) { + return addRelaysTag(new RelaysTag(relays)); + } + + public NIP57 addRelays(@NonNull List relays) { + return addRelaysList(relays.stream().map(Relay::new).toList()); + } + + public NIP57 addRelays(@NonNull String... relays) { + return addRelays(List.of(relays)); + } + /** * @param lnurl * @return @@ -188,29 +164,28 @@ public static GenericTag createAmountTag(@NonNull Integer amount) { /** * @param receiver - * @param relay + * @param relays * @param weight */ - public static GenericTag createZapTag(@NonNull PublicKey receiver, @NonNull Relay relay, Integer weight) { + public static GenericTag createZapTag(@NonNull PublicKey receiver, @NonNull List relays, Integer weight) { List attributes = new ArrayList<>(); var receiverAttr = new ElementAttribute("receiver", receiver.toString(), 57); - var relayAttr = new ElementAttribute("relay", relay.getUri(), 57); + var relayAttrs = relays.stream().map(relay -> new ElementAttribute("relay", relay.getUri(), 57)).toList(); if (weight != null) { var weightAttr = new ElementAttribute("weight", weight, 57); attributes.add(weightAttr); } attributes.add(receiverAttr); - attributes.add(relayAttr); - + attributes.addAll(relayAttrs); return new GenericTag(ZAP_TAG_NAME, 57, attributes); } /** * @param receiver - * @param relay + * @param relays */ - public static GenericTag createZapTag(@NonNull PublicKey receiver, @NonNull Relay relay) { - return createZapTag(receiver, relay, null); + public static GenericTag createZapTag(@NonNull PublicKey receiver, @NonNull List relays) { + return createZapTag(receiver, relays, null); } } diff --git a/nostr-java-api/src/main/java/nostr/api/NIP99.java b/nostr-java-api/src/main/java/nostr/api/NIP99.java index 79ed51430..b266cdc30 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP99.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP99.java @@ -4,7 +4,7 @@ import nostr.api.factory.impl.NIP99Impl.ClassifiedListingEventFactory; import nostr.event.BaseTag; import nostr.event.NIP99Event; -import nostr.event.impl.ClassifiedListingEvent.ClassifiedListing; +import nostr.event.impl.ClassifiedListing; import nostr.id.Identity; import java.util.List; @@ -14,9 +14,8 @@ public NIP99(@NonNull Identity sender) { setSender(sender); } - public NIP99 createClassifiedListingEvent(@NonNull List baseTags, @NonNull String content, @NonNull ClassifiedListing classifiedListing) { - var event = new ClassifiedListingEventFactory(getSender(), baseTags, content, classifiedListing).create(); - setEvent((T) event); + public NIP99 createClassifiedListingEvent(@NonNull List baseTags, String content, @NonNull ClassifiedListing classifiedListing) { + setEvent((T) new ClassifiedListingEventFactory(getSender(), baseTags, content, classifiedListing).create()); return this; } } diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP57Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP57Impl.java new file mode 100644 index 000000000..f3f1086a9 --- /dev/null +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP57Impl.java @@ -0,0 +1,80 @@ +package nostr.api.factory.impl; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import nostr.api.factory.EventFactory; +import nostr.base.PublicKey; +import nostr.base.Relay; +import nostr.event.BaseTag; +import nostr.event.impl.ZapReceipt; +import nostr.event.impl.ZapReceiptEvent; +import nostr.event.impl.ZapRequest; +import nostr.event.impl.ZapRequestEvent; +import nostr.event.tag.AddressTag; +import nostr.event.tag.EventTag; +import nostr.event.tag.PubKeyTag; +import nostr.event.tag.RelaysTag; +import nostr.id.Identity; + +import java.util.List; +import java.util.stream.Stream; + +public class NIP57Impl { + + @Data + @EqualsAndHashCode(callSuper = false) + public static class ZapRequestEventFactory extends EventFactory { + private final ZapRequest zapRequest; + private final PubKeyTag recipientKey; + + public ZapRequestEventFactory(@NonNull Identity sender, @NonNull PublicKey recipientPubKey, List tags, String content, @NonNull ZapRequest zapRequest) { + super(sender, tags, content); + this.zapRequest = zapRequest; + this.recipientKey = new PubKeyTag(recipientPubKey); + } + + public ZapRequestEventFactory(@NonNull Identity sender, @NonNull PublicKey recipientPubKey, List tags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull RelaysTag relaysTag) { + this(sender, recipientPubKey, tags, content, new ZapRequest(relaysTag, amount, lnUrl)); + } + + public ZapRequestEventFactory(@NonNull Identity sender, @NonNull PublicKey recipientPubKey, List tags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull List relays) { + this(sender, recipientPubKey, tags, content, amount, lnUrl, new RelaysTag(relays)); + } + + public ZapRequestEventFactory(@NonNull Identity sender, @NonNull PublicKey recipientPubKey, List tags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull String... relaysTags) { + this(sender, recipientPubKey, tags, content, amount, lnUrl, Stream.of(relaysTags).map(Relay::new).toList()); + } + + @Override + public ZapRequestEvent create() { + return new ZapRequestEvent(getSender(), recipientKey, getTags(), getContent(), zapRequest); + } + } + + @Data + @EqualsAndHashCode(callSuper = false) + public static class ZapReceiptEventFactory extends EventFactory { + private final ZapReceipt zapReceipt; + private final PubKeyTag zapRequestPubKeyTag; + private final EventTag zapReceiptEventTag; + private final AddressTag zapRequestAddressTag; + + public ZapReceiptEventFactory(@NonNull Identity sender, List tags, @NonNull PubKeyTag zapRequestPubKeyTag, EventTag zapReceiptEventTag, AddressTag zapReceiptAddressTag, ZapReceipt zapReceipt) { + super(sender, tags, null); + this.zapReceipt = zapReceipt; + this.zapRequestPubKeyTag = zapRequestPubKeyTag; + this.zapReceiptEventTag = zapReceiptEventTag; + this.zapRequestAddressTag = zapReceiptAddressTag; + } + + public ZapReceiptEventFactory(@NonNull Identity sender, List tags, @NonNull PubKeyTag zapRequestPubKeyTag, EventTag zapReceiptEventTag, AddressTag zapReceiptAddressTag, @NonNull String bolt11, @NonNull String descriptionSha256, @NonNull String preimage) { + this(sender, tags, zapRequestPubKeyTag, zapReceiptEventTag, zapReceiptAddressTag, new ZapReceipt(bolt11, descriptionSha256, preimage)); + } + + @Override + public ZapReceiptEvent create() { + return new ZapReceiptEvent(getSender(), zapRequestPubKeyTag, zapReceiptEventTag, zapRequestAddressTag, zapReceipt); + } + } +} diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP99Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP99Impl.java index 15b5fcbe5..0b4142d80 100644 --- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP99Impl.java +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP99Impl.java @@ -2,11 +2,12 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; import nostr.api.factory.EventFactory; import nostr.event.BaseTag; import nostr.event.Kind; +import nostr.event.impl.ClassifiedListing; import nostr.event.impl.ClassifiedListingEvent; -import nostr.event.impl.ClassifiedListingEvent.ClassifiedListing; import nostr.id.Identity; import java.util.List; @@ -19,11 +20,11 @@ public static class ClassifiedListingEventFactory extends EventFactory baseTags, String content, ClassifiedListing classifiedListing) { + public ClassifiedListingEventFactory(@NonNull Identity sender, List baseTags, String content, @NonNull ClassifiedListing classifiedListing) { this(sender, Kind.CLASSIFIED_LISTING, baseTags, content, classifiedListing); } - public ClassifiedListingEventFactory(Identity sender, Kind kind, List baseTags, String content, ClassifiedListing classifiedListing) { + public ClassifiedListingEventFactory(@NonNull Identity sender, @NonNull Kind kind, List baseTags, String content, @NonNull ClassifiedListing classifiedListing) { super(sender, baseTags, content); this.kind = kind; this.classifiedListing = classifiedListing; diff --git a/nostr-java-event/src/main/java/nostr/event/Kind.java b/nostr-java-event/src/main/java/nostr/event/Kind.java index 5305b1f4f..d40e994d9 100644 --- a/nostr-java-event/src/main/java/nostr/event/Kind.java +++ b/nostr-java-event/src/main/java/nostr/event/Kind.java @@ -27,6 +27,8 @@ public enum Kind { CHANNEL_MESSAGE(42, "channel_message"), HIDE_MESSAGE(43, "hide_message"), MUTE_USER(44, "mute_user"), + ZAP_REQUEST(9734, "zap_request"), + ZAP_RECEIPT(9735, "zap_receipt"), REPLACEABLE_EVENT(10_000, "replaceable_event"), EPHEMEREAL_EVENT(20_000, "ephemereal_event"), CLIENT_AUTH(22_242, "authentication_of_clients_to_relays"), diff --git a/nostr-java-event/src/main/java/nostr/event/NIP99Event.java b/nostr-java-event/src/main/java/nostr/event/NIP99Event.java index 482b992aa..d7bbd61c2 100644 --- a/nostr-java-event/src/main/java/nostr/event/NIP99Event.java +++ b/nostr-java-event/src/main/java/nostr/event/NIP99Event.java @@ -1,6 +1,7 @@ package nostr.event; import lombok.NoArgsConstructor; +import lombok.NonNull; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; @@ -8,16 +9,11 @@ @NoArgsConstructor public abstract class NIP99Event extends GenericEvent { - - public NIP99Event(PublicKey pubKey, Kind kind, List tags) { - super(pubKey, kind, tags); - } - - public NIP99Event(PublicKey pubKey, Kind kind, List tags, String content) { - super(pubKey, kind, tags, content); + public NIP99Event(@NonNull PublicKey pubKey, Kind kind, List baseTags) { + this(pubKey, kind, baseTags, null); } - public NIP99Event(PublicKey sender, Integer kind, List tags, String content) { - super(sender, kind, tags, content); + public NIP99Event(@NonNull PublicKey pubKey, Kind kind, List baseTags, String content) { + super(pubKey, kind, baseTags, content); } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListing.java b/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListing.java new file mode 100644 index 000000000..9ff59354a --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListing.java @@ -0,0 +1,38 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import nostr.event.AbstractEventContent; +import nostr.event.tag.PriceTag; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ClassifiedListing extends AbstractEventContent { + @JsonIgnore + private String id; + + @JsonProperty + private String title; + + @JsonProperty + private String summary; + + @JsonProperty("published_at") + @EqualsAndHashCode.Exclude + private Long publishedAt; + + @JsonProperty + private String location; + + @JsonProperty("price") + private PriceTag priceTag; + + public ClassifiedListing(@NonNull String title, @NonNull String summary, @NonNull PriceTag priceTag) { + this.title = title; + this.summary = summary; + this.priceTag = priceTag; + } +} \ No newline at end of file diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java index 7cece3126..2db0f8ab0 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java @@ -1,64 +1,39 @@ package nostr.event.impl; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; import nostr.base.PublicKey; import nostr.base.annotation.Event; -import nostr.event.AbstractEventContent; import nostr.event.BaseTag; import nostr.event.Kind; import nostr.event.NIP99Event; -import nostr.event.json.serializer.ClassifiedListingSerializer; import nostr.event.tag.PriceTag; +import java.math.BigDecimal; import java.util.List; @Data @EqualsAndHashCode(callSuper = false) @Event(name = "ClassifiedListingEvent", nip = 99) public class ClassifiedListingEvent extends NIP99Event { - + @JsonIgnore private final ClassifiedListing classifiedListing; - - public ClassifiedListingEvent(PublicKey sender, List baseTags, String content, ClassifiedListing classifiedListing) { - this(sender, Kind.CLASSIFIED_LISTING, baseTags, content, classifiedListing); - } - - public ClassifiedListingEvent(PublicKey sender, Kind kind, List baseTags, String content, ClassifiedListing classifiedListing) { + public ClassifiedListingEvent(@NonNull PublicKey sender, @NonNull Kind kind, List baseTags, String content, @NonNull ClassifiedListing classifiedListing) { super(sender, kind, baseTags, content); this.classifiedListing = classifiedListing; } - @Data - @EqualsAndHashCode(callSuper = false) - @JsonSerialize(using = ClassifiedListingSerializer.class) - public static class ClassifiedListing extends AbstractEventContent { - @JsonProperty - private String id; - - @JsonProperty - private String title; - - @JsonProperty - private String summary; - - @JsonProperty("published_at") - @EqualsAndHashCode.Exclude - private Long publishedAt; - - @JsonProperty - private String location; + public ClassifiedListingEvent(@NonNull PublicKey sender, List baseTags, String content, @NonNull ClassifiedListing classifiedListing) { + this(sender, Kind.CLASSIFIED_LISTING, baseTags, content, classifiedListing); + } - @JsonProperty("price") - private PriceTag priceTags; + public ClassifiedListingEvent(@NonNull PublicKey sender, List baseTags, String content, @NonNull String title, @NonNull String summary, @NonNull PriceTag priceTag) { + this(sender, Kind.CLASSIFIED_LISTING, baseTags, content, new ClassifiedListing(title, summary, priceTag)); + } - public ClassifiedListing(@NonNull String title, @NonNull String summary, @NonNull PriceTag priceTags) { - this.title = title; - this.summary = summary; - this.priceTags = priceTags; - } + public ClassifiedListingEvent(@NonNull PublicKey sender, List baseTags, String content, @NonNull String title, @NonNull String summary, @NonNull BigDecimal number, @NonNull String currency, @NonNull String frequency) { + this(sender, Kind.CLASSIFIED_LISTING, baseTags, content, new ClassifiedListing(title, summary, new PriceTag(number, currency, frequency))); } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ZapReceipt.java b/nostr-java-event/src/main/java/nostr/event/impl/ZapReceipt.java new file mode 100644 index 000000000..5cd6f5cf6 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ZapReceipt.java @@ -0,0 +1,35 @@ +package nostr.event.impl; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import nostr.event.AbstractEventContent; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ZapReceipt extends AbstractEventContent { + @JsonIgnore + private String id; + + @JsonProperty + private String bolt11; + + @JsonProperty + private String descriptionSha256; + + @JsonProperty + private String preimage; + + public ZapReceipt(@NonNull String bolt11, @NonNull String descriptionSha256, String preimage) { + this.descriptionSha256 = descriptionSha256; + this.bolt11 = bolt11; + this.preimage = preimage; + } + + public ZapReceipt(@NonNull String bolt11, @NonNull String descriptionSha256) { + this(bolt11, descriptionSha256, null); + } +} \ No newline at end of file diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ZapReceiptEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ZapReceiptEvent.java new file mode 100644 index 000000000..9814ae717 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ZapReceiptEvent.java @@ -0,0 +1,44 @@ + +package nostr.event.impl; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.base.Relay; +import nostr.base.annotation.Event; +import nostr.event.Kind; +import nostr.event.tag.*; + +import java.util.Optional; + +@Getter +@EqualsAndHashCode(callSuper = false) +@Event(name = "ZapReceiptEvent", nip = 57) +public class ZapReceiptEvent extends GenericEvent { + @JsonIgnore + private final ZapReceipt zapReceipt; + + public ZapReceiptEvent(@NonNull PublicKey pubKey, @NonNull PubKeyTag zapRequestPubKeyTag, EventTag zapRequestEventTag, + AddressTag zapRequestAddressTag, @NonNull ZapReceipt zapReceipt) { + super(pubKey, Kind.ZAP_RECEIPT); + super.addTag(zapRequestPubKeyTag); + Optional.ofNullable(zapRequestEventTag).ifPresent(super::addTag); + Optional.ofNullable(zapRequestAddressTag).ifPresent(super::addTag); + this.zapReceipt = zapReceipt; + } + + public ZapReceiptEvent(@NonNull PublicKey pubKey, @NonNull String zapRequestPubKeyTag, String zapRequestEventTag, AddressTag zapRequestAddressTag, @NonNull ZapReceipt zapReceipt) { + this(pubKey, new PubKeyTag(new PublicKey(zapRequestPubKeyTag)), new EventTag(zapRequestEventTag), zapRequestAddressTag, zapReceipt); + } + + public ZapReceiptEvent(@NonNull PublicKey pubKey, @NonNull String zapRequestPubKeyTag, String zapRequestEventTag, String zapRequestAddressTag, String zapRequestIdentifier, String zapRequestRelayUri, @NonNull String bolt11, @NonNull String descriptionSha256, @NonNull String preimage) { + this(pubKey, zapRequestPubKeyTag, zapRequestEventTag, + new AddressTag(null, + new PublicKey(zapRequestAddressTag), + new IdentifierTag(zapRequestIdentifier), + new Relay(zapRequestRelayUri)), + new ZapReceipt(bolt11, descriptionSha256, preimage)); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ZapRequest.java b/nostr-java-event/src/main/java/nostr/event/impl/ZapRequest.java new file mode 100644 index 000000000..bc62ffa86 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ZapRequest.java @@ -0,0 +1,31 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import nostr.event.AbstractEventContent; +import nostr.event.tag.RelaysTag; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ZapRequest extends AbstractEventContent { + @JsonIgnore + private String id; + + @JsonProperty("relays") + private RelaysTag relaysTag; + + @JsonProperty + private Long amount; + + @JsonProperty("lnurl") + private String lnUrl; + + public ZapRequest(@NonNull RelaysTag relaysTag, @NonNull Long amount, @NonNull String lnUrl) { + this.relaysTag = relaysTag; + this.amount = amount; + this.lnUrl = lnUrl; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ZapRequestEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ZapRequestEvent.java new file mode 100644 index 000000000..85ebab803 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/ZapRequestEvent.java @@ -0,0 +1,43 @@ + +package nostr.event.impl; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.base.Relay; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.tag.PubKeyTag; +import nostr.event.tag.RelaysTag; + +import java.util.Arrays; +import java.util.List; + +@Getter +@EqualsAndHashCode(callSuper = false) +@Event(name = "ZapRequestEvent", nip = 57) +public class ZapRequestEvent extends GenericEvent { + @JsonIgnore + private final ZapRequest zapRequest; + + public ZapRequestEvent(@NonNull PublicKey senderPubKey, @NonNull PubKeyTag recipientPubKey, List tags, String content, @NonNull ZapRequest zapRequest) { + super(senderPubKey, Kind.ZAP_REQUEST, tags, content); + super.addTag(recipientPubKey); + this.zapRequest = zapRequest; + } + + public ZapRequestEvent(@NonNull String senderPubKey, @NonNull PubKeyTag recipientPubKey, List tags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull RelaysTag relaysTag) { + this(new PublicKey(senderPubKey), recipientPubKey, tags, content, new ZapRequest(relaysTag, amount, lnUrl)); + } + + public ZapRequestEvent(@NonNull String senderPubKey, @NonNull String recipientPubKey, List tags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull List relays) { + this(senderPubKey, new PubKeyTag(new PublicKey(recipientPubKey)), tags, content, amount, lnUrl, new RelaysTag(relays)); + } + + public ZapRequestEvent(@NonNull String senderPubKey, @NonNull String recipientPubKey, List tags, String content, @NonNull Long amount, @NonNull String lnUrl, @NonNull String... relays) { + this(senderPubKey, recipientPubKey, tags, content, amount, lnUrl, Arrays.stream(relays).map(Relay::new).toList()); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/ClassifiedListingSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/ClassifiedListingSerializer.java deleted file mode 100644 index 851508a3d..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/ClassifiedListingSerializer.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.event.json.serializer; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import nostr.event.impl.ClassifiedListingEvent.ClassifiedListing; - -import java.io.IOException; - -public class ClassifiedListingSerializer extends JsonSerializer { - private final PriceTagSerializer priceTagSerializer; - - public ClassifiedListingSerializer() { - this.priceTagSerializer = new PriceTagSerializer(); - } - - @Override - public void serialize(ClassifiedListing classifiedListing, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeStartObject(); - jsonGenerator.writeStringField("id", classifiedListing.getId()); - jsonGenerator.writeStringField("title", classifiedListing.getTitle()); - jsonGenerator.writeStringField("summary", classifiedListing.getSummary()); - jsonGenerator.writeNumberField("publishedAt", classifiedListing.getPublishedAt()); - jsonGenerator.writeStringField("location", classifiedListing.getLocation()); - jsonGenerator.writeFieldName("price"); - priceTagSerializer.serialize(classifiedListing.getPriceTags(), jsonGenerator, serializerProvider); - jsonGenerator.writeEndObject(); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/PriceTagSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/PriceTagSerializer.java deleted file mode 100644 index 21d102e17..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/PriceTagSerializer.java +++ /dev/null @@ -1,20 +0,0 @@ -package nostr.event.json.serializer; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import nostr.event.tag.PriceTag; - -import java.io.IOException; - -public class PriceTagSerializer extends JsonSerializer { - - @Override - public void serialize(PriceTag priceTag, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeStartArray(); - jsonGenerator.writeNumber(priceTag.getNumber()); - jsonGenerator.writeString(priceTag.getCurrency()); - jsonGenerator.writeString(priceTag.getFrequency()); - jsonGenerator.writeEndArray(); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java new file mode 100644 index 000000000..626e31c49 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java @@ -0,0 +1,26 @@ +package nostr.event.json.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import lombok.NonNull; +import lombok.SneakyThrows; +import nostr.event.tag.RelaysTag; + +import java.io.IOException; + +public class RelaysTagSerializer extends JsonSerializer { + + @Override + public void serialize(@NonNull RelaysTag relaysTag, @NonNull JsonGenerator jsonGenerator, @NonNull SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeStartArray(); + jsonGenerator.writeFieldName("relays"); + relaysTag.getRelays().forEach(json -> writeString(jsonGenerator, json.getUri())); + jsonGenerator.writeEndArray(); + } + + @SneakyThrows + private static void writeString(JsonGenerator jsonGenerator, String json) { + jsonGenerator.writeString(json); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java b/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java index 463bd57cd..3725174e7 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java @@ -1,13 +1,13 @@ package nostr.event.tag; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import nostr.base.annotation.Tag; import nostr.event.BaseTag; -import nostr.event.json.serializer.PriceTagSerializer; import java.math.BigDecimal; @@ -16,9 +16,14 @@ @EqualsAndHashCode(callSuper = true) @Tag(code = "price", nip = 99) @RequiredArgsConstructor -@JsonSerialize(using = PriceTagSerializer.class) +@JsonPropertyOrder({"number", "currency", "frequency"}) public class PriceTag extends BaseTag { + @JsonProperty private final BigDecimal number; + + @JsonProperty private final String currency; + + @JsonProperty private final String frequency; } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java b/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java new file mode 100644 index 000000000..10fd01baf --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java @@ -0,0 +1,30 @@ +package nostr.event.tag; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import nostr.base.Relay; +import nostr.base.annotation.Tag; +import nostr.event.BaseTag; +import nostr.event.json.serializer.RelaysTagSerializer; + +import java.util.List; + +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@Tag(code = "relays", nip = 57) +@JsonSerialize(using = RelaysTagSerializer.class) +public class RelaysTag extends BaseTag { + private final List relays; + + public RelaysTag(@NonNull List relays) { + this.relays = relays; + } + + public RelaysTag(@NonNull Relay... relays) { + this(List.of(relays)); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java index ab31f6a83..c57e1e274 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java @@ -7,6 +7,7 @@ import nostr.api.NIP15; import nostr.api.NIP32; import nostr.api.NIP44; +import nostr.api.NIP57; import nostr.base.ElementAttribute; import nostr.base.PrivateKey; import nostr.base.PublicKey; @@ -20,6 +21,8 @@ import nostr.event.impl.NostrMarketplaceEvent; import nostr.event.impl.NostrMarketplaceEvent.Product.Spec; import nostr.event.impl.TextNoteEvent; +import nostr.event.impl.ZapReceiptEvent; +import nostr.event.impl.ZapRequestEvent; import nostr.id.Identity; import nostr.util.NostrException; import org.junit.jupiter.api.Assertions; @@ -266,6 +269,79 @@ public void testNIP32CreateLabel2() { Assertions.assertTrue(label.getAttributes().contains(new ElementAttribute("param2", "{\\\"article\\\":\\\"the\\\"}", 32)), "{\\\"article\\\":\\\"the\\\"}"); } + @Test + void testNIP57CreateZapRequestEvent() throws NostrException { + System.out.println("testNIP57CreateZapRequestEvent"); + + Identity sender = Identity.generateRandomIdentity(); + List baseTags = new ArrayList(); + PublicKey recipient = Identity.generateRandomIdentity().getPublicKey(); + var nip57 = new NIP57(sender); + final String ZAP_REQUEST_CONTENT = "zap request content"; + final Long AMOUNT = 1232456L; + final String LNURL = "lnUrl"; + final String RELAYS_TAG = "relaystag"; + ZapRequestEvent instance = nip57.createZapRequestEvent(recipient, baseTags, ZAP_REQUEST_CONTENT, AMOUNT, LNURL, RELAYS_TAG).getEvent(); + instance.update(); + + Assertions.assertNotNull(instance.getId()); + Assertions.assertNotNull(instance.getCreatedAt()); + Assertions.assertNotNull(instance.getContent()); + Assertions.assertNull(instance.getSignature()); + + Assertions.assertNotNull(instance.getZapRequest()); + Assertions.assertNotNull(instance.getZapRequest().getRelaysTag()); + Assertions.assertNotNull(instance.getZapRequest().getAmount()); + Assertions.assertNotNull(instance.getZapRequest().getLnUrl()); + + Assertions.assertEquals(ZAP_REQUEST_CONTENT, instance.getContent()); + Assertions.assertTrue(instance.getZapRequest().getRelaysTag().getRelays().stream().anyMatch(relay -> relay.getUri().equals(RELAYS_TAG))); + Assertions.assertEquals(AMOUNT, instance.getZapRequest().getAmount()); + Assertions.assertEquals(LNURL, instance.getZapRequest().getLnUrl()); + + final String bech32 = instance.toBech32(); + Assertions.assertNotNull(bech32); + Assertions.assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); + } + + @Test + void testNIP57CreateZapReceiptEvent() throws NostrException { + System.out.println("testNIP57CreateZapReceiptEvent"); + + Identity sender = Identity.generateRandomIdentity(); + List baseTags = new ArrayList(); + String zapRequestPubKeyTag = Identity.generateRandomIdentity().getPublicKey().toString(); + String zapRequestEventTag = Identity.generateRandomIdentity().getPublicKey().toString(); + String zapRequestAddressTag = Identity.generateRandomIdentity().getPublicKey().toString(); + final String ZAP_RECEIPT_IDENTIFIER = "ipsum"; + final String ZAP_RECEIPT_RELAY_URI = "requestRelayUrl"; + final String BOLT_11 = "bolt11"; + final String DESCRIPTION_SHA256 = "descriptionSha256"; + final String PRE_IMAGE = "preimage"; + var nip57 = new NIP57(sender); + + ZapReceiptEvent instance = nip57.createZapReceiptEvent(zapRequestPubKeyTag, baseTags, zapRequestEventTag, zapRequestAddressTag, ZAP_RECEIPT_IDENTIFIER, ZAP_RECEIPT_RELAY_URI, BOLT_11, DESCRIPTION_SHA256, PRE_IMAGE).getEvent(); + instance.update(); + + Assertions.assertNotNull(instance.getId()); + Assertions.assertNotNull(instance.getCreatedAt()); + Assertions.assertNull(instance.getSignature()); + + + Assertions.assertNotNull(instance.getZapReceipt()); + Assertions.assertNotNull(instance.getZapReceipt().getBolt11()); + Assertions.assertNotNull(instance.getZapReceipt().getDescriptionSha256()); + Assertions.assertNotNull(instance.getZapReceipt().getPreimage()); + + Assertions.assertEquals(BOLT_11, instance.getZapReceipt().getBolt11()); + Assertions.assertEquals(DESCRIPTION_SHA256, instance.getZapReceipt().getDescriptionSha256()); + Assertions.assertEquals(PRE_IMAGE, instance.getZapReceipt().getPreimage()); + + final String bech32 = instance.toBech32(); + Assertions.assertNotNull(bech32); + Assertions.assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); + } + private Stall createStall() { // Create the county list diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP57ImplTest.java b/nostr-java-test/src/test/java/nostr/test/event/NIP57ImplTest.java new file mode 100644 index 000000000..b6f9b11bf --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/NIP57ImplTest.java @@ -0,0 +1,39 @@ +package nostr.test.event; + +import nostr.api.factory.impl.NIP57Impl.ZapRequestEventFactory; +import nostr.base.PublicKey; +import nostr.event.BaseTag; +import nostr.id.Identity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +public class NIP57ImplTest { + @Test + void testNIP57CreateZapRequestEventFactory() { + System.out.println("testNIP57CreateZapRequestEventFactories"); + + Identity sender = Identity.generateRandomIdentity(); + List baseTags = new ArrayList(); + PublicKey recipient = Identity.generateRandomIdentity().getPublicKey(); + final String ZAP_REQUEST_CONTENT = "zap request content"; + final Long AMOUNT = 1232456L; + final String LNURL = "lnUrl"; + final String RELAYS_TAG = "relaystag"; + + ZapRequestEventFactory instance = new ZapRequestEventFactory(sender, recipient, baseTags, ZAP_REQUEST_CONTENT, AMOUNT, LNURL, RELAYS_TAG); + + Assertions.assertNotNull(instance.getIdentity()); + Assertions.assertNotNull(instance.getTags()); + Assertions.assertNotNull(instance.getContent()); + Assertions.assertNotNull(instance.getZapRequest()); + Assertions.assertNotNull(instance.getRecipientKey()); + + Assertions.assertTrue(instance.getZapRequest().getRelaysTag().getRelays().stream().anyMatch(relay -> relay.getUri().equals(RELAYS_TAG))); + Assertions.assertEquals(ZAP_REQUEST_CONTENT, instance.getContent()); + Assertions.assertEquals(LNURL, instance.getZapRequest().getLnUrl()); + Assertions.assertEquals(AMOUNT, instance.getZapRequest().getAmount()); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP99ImplTest.java b/nostr-java-test/src/test/java/nostr/test/event/NIP99ImplTest.java new file mode 100644 index 000000000..d02b0399f --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/NIP99ImplTest.java @@ -0,0 +1,105 @@ +package nostr.test.event; + +import nostr.api.NIP99; +import nostr.event.BaseTag; +import nostr.event.impl.ClassifiedListing; +import nostr.event.impl.ClassifiedListingEvent; +import nostr.event.impl.GenericTag; +import nostr.event.tag.PriceTag; +import nostr.id.Identity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +class NIP99ImplTest { + + @Test + void testNIP99CreateClassifiedListingEventWithAllOptionalParameters() { + System.out.println("testNIP99CreateClassifiedListingEventWithAllOptionalParameters"); + + Identity sender = Identity.generateRandomIdentity(); + List baseTags = new ArrayList(); + final String CONTENT = "ClassifiedListingEvent unit test content"; + + var nip99 = new NIP99(sender); + String unitTestTitle = "unit test title"; + String unitTestSummary = "unit test summary"; + String currency = "BTC"; + String month = "MONTH"; + String location = "pangea"; + Long publishedAt = 1716513986268L; + + ClassifiedListing classifiedListing = new ClassifiedListing( + unitTestTitle, + unitTestSummary, + new PriceTag(BigDecimal.valueOf(11111), currency, month) + ); + classifiedListing.setLocation(location); + classifiedListing.setPublishedAt(publishedAt); + + ClassifiedListingEvent instance = nip99.createClassifiedListingEvent(baseTags, CONTENT, classifiedListing).getEvent(); + instance.update(); + + Assertions.assertNotNull(instance.getId()); + } + + @Test + void testNIP99CreateClassifiedListingEventWithoutOptionalParameters() { + System.out.println("testNIP99CreateClassifiedListingEventWithoutOptionalParameters"); + + Identity sender = Identity.generateRandomIdentity(); + List baseTags = new ArrayList(); + final String CONTENT = "ClassifiedListingEvent unit test content"; + + var nip99 = new NIP99(sender); + String unitTestTitle = "unit test title"; + String unitTestSummary = "unit test summary"; + String currency = "BTC"; + String month = "MONTH"; + + ClassifiedListing classifiedListing = new ClassifiedListing( + unitTestTitle, + unitTestSummary, + new PriceTag(BigDecimal.valueOf(11111), currency, month) + ); + + ClassifiedListingEvent instance = nip99.createClassifiedListingEvent(baseTags, CONTENT, classifiedListing).getEvent(); + instance.update(); + + Assertions.assertNotNull(instance.getId()); + } + + @Test + void testNIP99CreateClassifiedListingEventWithDuplicateParameters() { + System.out.println("testNIP99CreateClassifiedListingEventWithDuplicateParameters"); + + Identity sender = Identity.generateRandomIdentity(); + List baseTags = new ArrayList(); + final String CONTENT = "ClassifiedListingEvent unit test content"; + + var nip99 = new NIP99(sender); + String unitTestTitle = "unit test title"; + String unitTestSummary = "unit test summary"; + String currency = "BTC"; + String month = "MONTH"; + String location = "pangea"; + Long publishedAt = 1716513986268L; + + ClassifiedListing classifiedListing = new ClassifiedListing( + unitTestTitle, + unitTestSummary, + new PriceTag(BigDecimal.valueOf(11111), currency, month) + ); + classifiedListing.setLocation(location); + classifiedListing.setPublishedAt(publishedAt); + + baseTags.add(GenericTag.create("published_at", 99, String.valueOf(publishedAt))); + ClassifiedListingEvent instance = nip99.createClassifiedListingEvent(baseTags, CONTENT, classifiedListing).getEvent(); + instance.update(); + + Assertions.assertNotNull(instance.getId()); + } +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ZapReceiptEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ZapReceiptEventTest.java new file mode 100644 index 000000000..b356126e2 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/ZapReceiptEventTest.java @@ -0,0 +1,47 @@ +package nostr.test.event; + +import nostr.base.PublicKey; +import nostr.event.BaseTag; +import nostr.event.impl.GenericTag; +import nostr.event.impl.ZapReceiptEvent; +import nostr.event.tag.AddressTag; +import nostr.event.tag.IdentifierTag; +import nostr.id.Identity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +class ZapReceiptEventTest { + @Test + void testConstructZapReceiptEvent() { + System.out.println("testConstructZapReceiptEvent"); + + PublicKey sender = Identity.generateRandomIdentity().getPublicKey(); + String zapRequestPubKeyTag = Identity.generateRandomIdentity().getPublicKey().toString(); + String zapRequestEventTag = Identity.generateRandomIdentity().getPublicKey().toString(); + String zapRequestAddressTag = Identity.generateRandomIdentity().getPublicKey().toString(); + final String ZAP_RECEIPT_IDENTIFIER = "ipsum"; + final String ZAP_RECEIPT_RELAY_URI = "requestRelayUrl"; + final String BOLT_11 = "bolt11"; + final String DESCRIPTION_SHA256 = "descriptionSha256"; + final String PRE_IMAGE = "preimage"; + + ZapReceiptEvent instance = new ZapReceiptEvent(sender, zapRequestPubKeyTag, zapRequestEventTag, zapRequestAddressTag, ZAP_RECEIPT_IDENTIFIER, ZAP_RECEIPT_RELAY_URI, BOLT_11, DESCRIPTION_SHA256, PRE_IMAGE); + + Assertions.assertNotNull(instance.getZapReceipt()); + Assertions.assertNotNull(instance.getZapReceipt().getBolt11()); + Assertions.assertNotNull(instance.getZapReceipt().getDescriptionSha256()); + Assertions.assertNotNull(instance.getZapReceipt().getPreimage()); + + Assertions.assertTrue(instance.getTags().stream().filter(AddressTag.class::isInstance).map(AddressTag.class::cast).map(addressTag -> addressTag.getPublicKey().toString()).anyMatch(zapRequestAddressTag::equals)); + + Assertions.assertTrue(instance.getTags().stream().filter(AddressTag.class::isInstance).map(AddressTag.class::cast).map(addressTag -> addressTag.getRelay().getUri()).anyMatch(ZAP_RECEIPT_RELAY_URI::equals)); + + Assertions.assertTrue(instance.getTags().stream().filter(AddressTag.class::isInstance).map(AddressTag.class::cast).map(addressTag -> addressTag.getIdentifierTag().getId()).anyMatch(ZAP_RECEIPT_IDENTIFIER::equals)); + + Assertions.assertEquals(BOLT_11, instance.getZapReceipt().getBolt11()); + Assertions.assertEquals(DESCRIPTION_SHA256, instance.getZapReceipt().getDescriptionSha256()); + Assertions.assertEquals(PRE_IMAGE, instance.getZapReceipt().getPreimage()); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/ZapRequestEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ZapRequestEventTest.java new file mode 100644 index 000000000..bf234e61e --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/ZapRequestEventTest.java @@ -0,0 +1,34 @@ +package nostr.test.event; + +import nostr.event.BaseTag; +import nostr.event.impl.ZapRequestEvent; +import nostr.id.Identity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +class ZapRequestEventTest { + @Test + void testConstructZapRequestEvent() { + System.out.println("testConstructZapRequestEvent"); + String sender = Identity.generateRandomIdentity().getPublicKey().toString(); + String recipient = Identity.generateRandomIdentity().getPublicKey().toString(); + List baseTags = new ArrayList(); + final String ZAP_REQUEST_CONTENT = "zap request content"; + final Long AMOUNT = 1232456L; + final String LNURL = "lnUrl"; + final String RELAYS_TAG = "relaystag"; + ZapRequestEvent instance = new ZapRequestEvent(sender, recipient, baseTags, ZAP_REQUEST_CONTENT, AMOUNT, LNURL, RELAYS_TAG); + + Assertions.assertNotNull(instance.getTags()); + Assertions.assertNotNull(instance.getContent()); + Assertions.assertNotNull(instance.getZapRequest()); + + Assertions.assertTrue(instance.getZapRequest().getRelaysTag().getRelays().stream().anyMatch(relay -> relay.getUri().equals(RELAYS_TAG))); + Assertions.assertEquals(ZAP_REQUEST_CONTENT, instance.getContent()); + Assertions.assertEquals(LNURL, instance.getZapRequest().getLnUrl()); + Assertions.assertEquals(AMOUNT, instance.getZapRequest().getAmount()); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java index 075878d60..07a8d6e4f 100644 --- a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java +++ b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java @@ -1,5 +1,7 @@ package nostr.test.json; +import com.fasterxml.jackson.core.JsonProcessingException; +import nostr.api.NIP01; import nostr.base.Command; import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; @@ -12,10 +14,12 @@ import nostr.event.impl.Filters; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; +import nostr.event.json.codec.BaseEventEncoder; import nostr.event.json.codec.BaseMessageDecoder; import nostr.event.json.codec.BaseMessageEncoder; import nostr.event.json.codec.BaseTagDecoder; import nostr.event.json.codec.FiltersEncoder; +import nostr.event.json.codec.GenericEventDecoder; import nostr.event.json.codec.GenericTagDecoder; import nostr.event.list.EventList; import nostr.event.list.FiltersList; @@ -45,11 +49,11 @@ public void testBaseReqMessageDecoder() { System.out.println("testBaseReqMessageDecoder"); final String parseTarget = - "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"kinds\": [1], " + - "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + - "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + "[\"REQ\", " + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + + "{\"kinds\": [1], " + + "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + + "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; final var message = new BaseMessageDecoder(parseTarget).decode(); @@ -75,8 +79,8 @@ public void testBaseReqMessageEncoder() { final var filtersList = new FiltersList(); var publicKey = Identity.generateRandomIdentity().getPublicKey(); - filtersList.add(Filters.builder().authors(new PublicKeyList(publicKey)).kinds(new KindList(3,5)).build()); - filtersList.add(Filters.builder().kinds(new KindList(0,1)).build()); + filtersList.add(Filters.builder().authors(new PublicKeyList(publicKey)).kinds(new KindList(3, 5)).build()); + filtersList.add(Filters.builder().kinds(new KindList(0, 1)).build()); filtersList.add(Filters.builder().referencedEvents(new EventList(new BaseEvent.ProxyEvent("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"))).build()); final var reqMessage = new ReqMessage(publicKey.toString(), filtersList); @@ -85,12 +89,12 @@ public void testBaseReqMessageEncoder() { var jsonMessage = encoder.encode(); - var jsonMsg = jsonMessage.substring(1, jsonMessage.length()-1); + var jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1); var parts = jsonMsg.split(","); Assertions.assertEquals("\"REQ\"", parts[0]); Assertions.assertEquals("\"" + publicKey.toString() + "\"", parts[1]); Assertions.assertFalse(parts[2].startsWith("[")); - Assertions.assertFalse(parts[parts.length-1].endsWith("]")); + Assertions.assertFalse(parts[parts.length - 1].endsWith("]")); var message = new BaseMessageDecoder(jsonMessage).decode(); @@ -102,17 +106,17 @@ public void testBaseEventMessageDecoder() { System.out.println("testBaseEventMessageDecoder"); final String parseTarget - = "[\"EVENT\"," - + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\"," - + "{" - + "\"content\":\"直んないわ。まあええか\"," - + "\"created_at\":1686199583," - + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," - + "\"kind\":1," - + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," - + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," - + "\"tags\":[]" - + "}]"; + = "[\"EVENT\"," + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\"," + + "{" + + "\"content\":\"直んないわ。まあええか\"," + + "\"created_at\":1686199583," + + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," + + "\"kind\":1," + + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," + + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," + + "\"tags\":[]" + + "}]"; final var message = new BaseMessageDecoder(parseTarget).decode(); @@ -130,20 +134,20 @@ public void testBaseEventMessageMarkerDecoder() { System.out.println("testBaseEventMessageMarkerDecoder"); String json = "[" - + "\"EVENT\"," - + "\"temp20230627\"," - + "{" - + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," - + "\"kind\":1," - + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," - + "\"created_at\":1687765220," - + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," - + "\"tags\":[" - + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," - + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" - + "]," - + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" - + "}]"; + + "\"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(); @@ -171,6 +175,59 @@ public void testGenericTagDecoder() { assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString())); } + @Test + public void testClassifiedListingTagSerializer() throws NostrException, JsonProcessingException { + System.out.println("testClassifiedListingSerializer"); + var classifiedListingEventJson = "{" + + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," + + "\"kind\":30402," + + "\"content\":\"content ipsum\"," + + "\"pubkey\":\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"," + + "\"created_at\":1687765220," + + "\"tags\":[" + + "[\"p\",\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"]," + + "[\"title\",\"title ipsum\"]," + + "[\"summary\",\"summary ipsum\"]," + + "[\"published_at\",\"1687765220\"]," + + "[\"location\",\"location ipsum\"]," + + "[\"price\",\"11111\",\"BTC\",\"1\"]]," + + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" + + "}]"; + + GenericEvent event = new GenericEventDecoder(classifiedListingEventJson).decode(); + EventMessage message = NIP01.createEventMessage(event, "1"); + assertEquals("{\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\",\"kind\":30402,\"content\":\"content ipsum\",\"pubkey\":\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\",\"created_at\":1687765220,\"tags\":[[\"p\",\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"],[\"title\",\"title ipsum\"],[\"summary\",\"summary ipsum\"],[\"published_at\",\"1687765220\"],[\"location\",\"location ipsum\"],[\"price\",\"11111\",\"BTC\",\"1\"]],\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"}", new BaseEventEncoder((BaseEvent) message.getEvent()).encode()); + + assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", event.getId()); + assertEquals(30402, event.getKind()); + assertEquals("content ipsum", event.getContent()); + assertEquals("ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de", event.getPubKey().toString()); + assertEquals(1687765220L, event.getCreatedAt()); + assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546", event.getSignature().toString()); + + List genericTags = event.getTags().stream() + .filter(GenericTag.class::isInstance) + .map(GenericTag.class::cast).toList(); + + List price = genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("price")).map(GenericTag::getAttributes).toList().get(0); + assertEquals("11111", price.get(0).getValue()); + assertEquals("BTC", price.get(1).getValue()); + assertEquals("1", price.get(2).getValue()); + + assertEquals("title ipsum", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); + + assertEquals("summary ipsum", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("summary")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); + + assertEquals("1687765220", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("published_at")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); + + assertEquals("location ipsum", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("location")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); + } + @Test public void testDeserializeTag() throws NostrException { System.out.println("testDeserializeTag");