feat: support rendering kind 20,21,22
feat: reply to non-text-note as kind 1111
This commit is contained in:
@ -12,12 +12,16 @@ const enum EventKind {
|
||||
SimpleChatMessage = 9, // NIP-29
|
||||
SealedRumor = 13, // NIP-59
|
||||
ChatRumor = 14, // NIP-24
|
||||
Photo = 20, // NIP-68
|
||||
Video = 21, // NIP-71
|
||||
ShortVideo = 22, // NIP-71
|
||||
PublicChatChannel = 40, // NIP-28
|
||||
PublicChatMetadata = 41, // NIP-28
|
||||
PublicChatMessage = 42, // NIP-28
|
||||
PublicChatMuteMessage = 43, // NIP-28
|
||||
PublicChatMuteUser = 44, // NIP-28
|
||||
SnortSubscriptions = 1000, // NIP-XX
|
||||
Comment = 1111, // NIP-22
|
||||
Polls = 6969, // NIP-69
|
||||
GiftWrap = 1059, // NIP-59
|
||||
FileHeader = 1063, // NIP-94
|
||||
|
@ -25,6 +25,7 @@ import { EventBuilder } from "./event-builder";
|
||||
import { findTag } from "./utils";
|
||||
import { Nip7Signer } from "./impl/nip7";
|
||||
import { Nip10 } from "./impl/nip10";
|
||||
import { Nip22 } from "./impl/nip22";
|
||||
|
||||
type EventBuilderHook = (ev: EventBuilder) => EventBuilder;
|
||||
|
||||
@ -195,12 +196,19 @@ export class EventPublisher {
|
||||
|
||||
/**
|
||||
* Reply to a note
|
||||
*
|
||||
* Replies to kind 1 notes are kind 1, otherwise kind 1111
|
||||
*/
|
||||
async reply(replyTo: TaggedNostrEvent, msg: string, fnExtra?: EventBuilderHook) {
|
||||
const eb = this.#eb(EventKind.TextNote);
|
||||
const kind = replyTo.kind === EventKind.TextNote ? EventKind.TextNote : EventKind.Comment;
|
||||
const eb = this.#eb(kind);
|
||||
eb.content(msg);
|
||||
|
||||
Nip10.replyTo(replyTo, eb);
|
||||
if (kind === EventKind.TextNote) {
|
||||
Nip10.replyTo(replyTo, eb);
|
||||
} else {
|
||||
Nip22.replyTo(replyTo, eb);
|
||||
}
|
||||
eb.processContent();
|
||||
fnExtra?.(eb);
|
||||
return await this.#sign(eb);
|
||||
@ -211,6 +219,7 @@ export class EventPublisher {
|
||||
eb.content(content);
|
||||
eb.tag(unwrap(NostrLink.fromEvent(evRef).toEventTag()));
|
||||
eb.tag(["p", evRef.pubkey]);
|
||||
eb.tag(["k", evRef.kind.toString()]);
|
||||
return await this.#sign(eb);
|
||||
}
|
||||
|
||||
|
35
packages/system/src/impl/nip22.ts
Normal file
35
packages/system/src/impl/nip22.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { findTag } from "../utils";
|
||||
import { EventBuilder, NostrEvent, NostrLink, NostrPrefix } from "../index";
|
||||
|
||||
export class Nip22 {
|
||||
/**
|
||||
* Get the root scope tag (E/A/I) or
|
||||
* create a root scope tag from the provided event
|
||||
*/
|
||||
static rootScopeOf(other: NostrEvent) {
|
||||
const linkOther = NostrLink.fromEvent(other);
|
||||
return other.tags.find(t => ["E", "A", "I"].includes(t[0])) ?? linkOther.toEventTagNip22(true)!;
|
||||
}
|
||||
|
||||
static replyTo(other: NostrEvent, eb: EventBuilder) {
|
||||
const linkOther = NostrLink.fromEvent(other);
|
||||
const rootScope = Nip22.rootScopeOf(other);
|
||||
const rootKind = ["K", findTag(other, "K") ?? other.kind.toString()];
|
||||
const rootAuthor = ["P", findTag(other, "P") ?? other.pubkey];
|
||||
|
||||
const replyScope = linkOther.toEventTagNip22(false);
|
||||
const replyKind = ["k", other.kind.toString()];
|
||||
const replyAuthor = ["p", other.pubkey];
|
||||
|
||||
if (rootScope === undefined || replyScope === undefined) {
|
||||
throw new Error("RootScope or ReplyScope are undefined!");
|
||||
}
|
||||
|
||||
eb.tag(rootScope);
|
||||
eb.tag(rootKind);
|
||||
eb.tag(rootAuthor);
|
||||
eb.tag(replyScope);
|
||||
eb.tag(replyKind);
|
||||
eb.tag(replyAuthor);
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ export * from "./encryption";
|
||||
export * from "./impl/nip4";
|
||||
export * from "./impl/nip7";
|
||||
export * from "./impl/nip10";
|
||||
export * from "./impl/nip22";
|
||||
export * from "./impl/nip44";
|
||||
export * from "./impl/nip46";
|
||||
export * from "./impl/nip57";
|
||||
|
@ -114,6 +114,32 @@ export class NostrLink implements ToNostrEventTag {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an event tag from this link as per NIP-22 (no marker position)
|
||||
*/
|
||||
toEventTagNip22(root?: boolean) {
|
||||
// emulate root flag by root marker
|
||||
root ??= this.marker === "root";
|
||||
|
||||
const suffix: Array<string> = [];
|
||||
if (this.relays && this.relays.length > 0) {
|
||||
suffix.push(this.relays[0]);
|
||||
}
|
||||
if (this.type === NostrPrefix.PublicKey || this.type === NostrPrefix.Profile) {
|
||||
return [root ? "P" : "p", this.id, ...suffix];
|
||||
} else if (this.type === NostrPrefix.Note || this.type === NostrPrefix.Event) {
|
||||
if (this.author) {
|
||||
if (suffix[0] === undefined) {
|
||||
suffix.push(""); // empty relay hint
|
||||
}
|
||||
suffix.push(this.author);
|
||||
}
|
||||
return [root ? "E" : "e", this.id, ...suffix];
|
||||
} else if (this.type === NostrPrefix.Address) {
|
||||
return [root ? "A" : "a", `${this.kind}:${this.author}:${this.id}`, ...suffix];
|
||||
}
|
||||
}
|
||||
|
||||
matchesEvent(ev: NostrEvent) {
|
||||
if (this.type === NostrPrefix.Address) {
|
||||
const dTag = findTag(ev, "d");
|
||||
@ -194,12 +220,19 @@ export class NostrLink implements ToNostrEventTag {
|
||||
static fromTag(tag: Array<string>, author?: string, kind?: number) {
|
||||
const relays = tag.length > 2 ? [tag[2]] : undefined;
|
||||
switch (tag[0]) {
|
||||
case "E": {
|
||||
return new NostrLink(NostrPrefix.Event, tag[1], kind, author ?? tag[3], relays, "root");
|
||||
}
|
||||
case "e": {
|
||||
return new NostrLink(NostrPrefix.Event, tag[1], kind, author ?? tag[4], relays, tag[3]);
|
||||
}
|
||||
case "p": {
|
||||
return new NostrLink(NostrPrefix.Profile, tag[1], kind, author, relays);
|
||||
}
|
||||
case "A": {
|
||||
const [kind, author, dTag] = tag[1].split(":");
|
||||
return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays, "root");
|
||||
}
|
||||
case "a": {
|
||||
const [kind, author, dTag] = tag[1].split(":");
|
||||
return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays, tag[3]);
|
||||
@ -242,7 +275,7 @@ export class NostrLink implements ToNostrEventTag {
|
||||
let relays = "relays" in ev ? ev.relays : undefined;
|
||||
const eventRelays = removeUndefined(
|
||||
ev.tags
|
||||
.filter(a => a[0] === "relays" || a[0] === "relay" || a[0] === "r")
|
||||
.filter(a => a[0] === "relays" || a[0] === "relay" || (a[0] === "r" && ev.kind == EventKind.Relays))
|
||||
.flatMap(a => a.slice(1).map(b => sanitizeRelayUrl(b))),
|
||||
);
|
||||
relays = appendDedupe(relays, eventRelays);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { equalProp } from "@snort/shared";
|
||||
import { FlatReqFilter } from "./query-optimizer";
|
||||
import { IMeta, NostrEvent, ReqFilter } from "./nostr";
|
||||
import { NostrEvent, ReqFilter } from "./nostr";
|
||||
|
||||
export function findTag(e: NostrEvent, tag: string) {
|
||||
const maybeTag = e.tags.find(evTag => {
|
||||
@ -9,6 +9,13 @@ export function findTag(e: NostrEvent, tag: string) {
|
||||
return maybeTag && maybeTag[1];
|
||||
}
|
||||
|
||||
export function findWholeTag(e: NostrEvent, tag: string) {
|
||||
const maybeTag = e.tags.find(evTag => {
|
||||
return evTag[0] === tag;
|
||||
});
|
||||
return maybeTag;
|
||||
}
|
||||
|
||||
export function reqFilterEq(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | ReqFilter): boolean {
|
||||
return (
|
||||
equalProp(a.ids, b.ids) &&
|
||||
|
Reference in New Issue
Block a user