bug: unmarked thread tag parsing

fixes: #386
This commit is contained in:
2023-06-13 12:24:57 +01:00
parent a7fa996e84
commit fdf3d855df
7 changed files with 117 additions and 115 deletions

View File

@ -1,14 +1,21 @@
import * as secp from "@noble/curves/secp256k1";
import * as utils from "@noble/curves/abstract/utils";
import { EventKind, HexKey, NostrEvent, Tag } from ".";
import { EventKind, HexKey, NostrEvent } from ".";
import base64 from "@protobufjs/base64";
import { sha256, unixNow } from "./Utils";
export interface Tag {
key: string
value?: string
relay?: string
marker?: string // NIP-10
}
export interface Thread {
root?: Tag;
replyTo?: Tag;
mentions: Array<Tag>;
pubKeys: Array<HexKey>;
root?: Tag
replyTo?: Tag
mentions: Array<Tag>
pubKeys: Array<HexKey>
}
export abstract class EventExt {
@ -73,6 +80,24 @@ export abstract class EventExt {
} as NostrEvent;
}
static parseTag(tag: Array<string>) {
if (tag.length < 1) {
throw new Error("Invalid tag, must have more than 2 items")
}
const ret = {
key: tag[0],
value: tag[1]
} as Tag;
switch (ret.key) {
case "e": {
ret.relay = tag.length > 2 ? tag[2] : undefined;
ret.marker = tag.length > 3 ? tag[3] : undefined;
break;
}
}
return ret;
}
static extractThread(ev: NostrEvent) {
const isThread = ev.tags.some(a => (a[0] === "e" && a[3] !== "mention") || a[0] == "a");
if (!isThread) {
@ -84,27 +109,27 @@ export abstract class EventExt {
mentions: [],
pubKeys: [],
} as Thread;
const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map((v, i) => new Tag(v, i));
const marked = eTags.some(a => a.Marker !== undefined);
const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map(a => EventExt.parseTag(a));
const marked = eTags.some(a => a.marker);
if (!marked) {
ret.root = eTags[0];
ret.root.Marker = shouldWriteMarkers ? "root" : undefined;
ret.root.marker = shouldWriteMarkers ? "root" : undefined;
if (eTags.length > 1) {
ret.replyTo = eTags[1];
ret.replyTo.Marker = shouldWriteMarkers ? "reply" : undefined;
ret.replyTo = eTags[eTags.length - 1];
ret.replyTo.marker = shouldWriteMarkers ? "reply" : undefined;
}
if (eTags.length > 2) {
ret.mentions = eTags.slice(2);
ret.mentions = eTags.slice(1, -1);
if (shouldWriteMarkers) {
ret.mentions.forEach(a => (a.Marker = "mention"));
ret.mentions.forEach(a => (a.marker = "mention"));
}
}
} else {
const root = eTags.find(a => a.Marker === "root");
const reply = eTags.find(a => a.Marker === "reply");
const root = eTags.find(a => a.marker === "root");
const reply = eTags.find(a => a.marker === "reply");
ret.root = root;
ret.replyTo = reply;
ret.mentions = eTags.filter(a => a.Marker === "mention");
ret.mentions = eTags.filter(a => a.marker === "mention");
}
ret.pubKeys = Array.from(new Set(ev.tags.filter(a => a[0] === "p").map(a => a[1])));
return ret;

View File

@ -214,7 +214,7 @@ export class EventPublisher {
const thread = EventExt.extractThread(replyTo);
if (thread) {
if (thread.root || thread.replyTo) {
eb.tag(["e", thread.root?.Event ?? thread.replyTo?.Event ?? "", "", "root"]);
eb.tag(["e", thread.root?.value ?? thread.replyTo?.value ?? "", "", "root"]);
}
eb.tag(["e", replyTo.id, replyTo.relays?.[0] ?? "", "reply"]);

View File

@ -1,88 +0,0 @@
import { HexKey, u256 } from "./Nostr";
import { unwrap } from "./Utils";
export default class Tag {
Original: string[];
Key: string;
Event?: u256;
PubKey?: HexKey;
Relay?: string;
Marker?: string;
Hashtag?: string;
DTag?: string;
ATag?: string;
Index: number;
Invalid: boolean;
LNURL?: string;
constructor(tag: string[], index: number) {
this.Original = tag;
this.Key = tag[0];
this.Index = index;
this.Invalid = false;
switch (this.Key) {
case "e": {
// ["e", <event-id>, <relay-url>, <marker>]
this.Event = tag[1];
this.Relay = tag.length > 2 ? tag[2] : undefined;
this.Marker = tag.length > 3 ? tag[3] : undefined;
if (!this.Event) {
this.Invalid = true;
}
break;
}
case "p": {
// ["p", <pubkey>]
this.PubKey = tag[1];
if (!this.PubKey) {
this.Invalid = true;
}
break;
}
case "d": {
this.DTag = tag[1];
break;
}
case "a": {
this.ATag = tag[1];
break;
}
case "t": {
this.Hashtag = tag[1];
break;
}
case "delegation": {
this.PubKey = tag[1];
break;
}
case "zap": {
this.LNURL = tag[1];
break;
}
}
}
ToObject(): string[] | null {
switch (this.Key) {
case "e": {
let ret = ["e", this.Event, this.Relay, this.Marker];
const trimEnd = ret.reverse().findIndex(a => a !== undefined);
ret = ret.reverse().slice(0, ret.length - trimEnd);
return <string[]>ret;
}
case "p": {
return this.PubKey ? ["p", this.PubKey] : null;
}
case "t": {
return ["t", unwrap(this.Hashtag)];
}
case "d": {
return ["d", unwrap(this.DTag)];
}
default: {
return this.Original;
}
}
}
}

View File

@ -8,7 +8,6 @@ export * from "./NostrSystem";
export { default as EventKind } from "./EventKind";
export * from "./Nostr";
export * from "./Links";
export { default as Tag } from "./Tag";
export * from "./Nips";
export * from "./RelayInfo";
export * from "./EventExt";

View File

@ -0,0 +1,62 @@
import { EventExt } from "../src/EventExt";
describe("NIP-10", () => {
it("should extract thread", () => {
const a = {
content: "This is the problem with Lightning....",
id: "868187063f...",
kind: 1,
created_at: 1,
pubkey: "test",
sig: "test",
"tags": [
["e", "cbf2375078..."],
["e", "977ac5d3b6..."],
["e", "8f99ca1363..."],
]
}
const b = {
"content": "This is a good point, but your ...",
"id": "434ad4a646...",
kind: 1,
created_at: 1,
pubkey: "test",
sig: "test",
"tags": [
["e", "cbf2375078..."],
["e", "868187063f..."],
["e", "6834ffc491..."],
]
}
const c = {
"content": "There is some middle ground ...",
"id": "6834ffc491...",
kind: 1,
created_at: 1,
pubkey: "test",
sig: "test",
"tags": [
["e", "cbf2375078...", "", "root"],
["e", "868187063f...", "", "reply"],
]
}
expect(EventExt.extractThread(a)).toMatchObject({
root: { key: "e", value: "cbf2375078...", marker: "root" },
replyTo: { key: "e", value: "8f99ca1363...", marker: "reply" },
mentions: [{ key: "e", value: "977ac5d3b6...", marker: "mention" }]
})
expect(EventExt.extractThread(b)).toMatchObject({
root: { key: "e", value: "cbf2375078...", marker: "root" },
replyTo: { key: "e", value: "6834ffc491...", marker: "reply" },
mentions: [{ key: "e", value: "868187063f...", marker: "mention" }]
})
expect(EventExt.extractThread(c)).toMatchObject({
root: { key: "e", value: "cbf2375078...", relay: "", marker: "root" },
replyTo: { key: "e", value: "868187063f...", relay: "", marker: "reply" },
mentions: []
})
})
})