Merge pull request #823 from PR0M3TH3AN/codex/extend-buildreactionevent-and-teach-videomodal-dogrjd

Add author metadata to reaction events
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-23 09:18:05 -04:00
commit 757c971d84
4 files changed
+237 -5

No files matched your search

+29 -4
View File
@@ -50,15 +50,38 @@ export async function publishVideoReaction(
)
: [];
const optionRelay =
typeof options.pointerRelay === "string" && options.pointerRelay.trim()
? options.pointerRelay.trim()
: "";
const pointerRelay =
typeof pointer.relay === "string" && pointer.relay.trim()
? pointer.relay.trim()
: optionRelay;
const pointerTag =
pointer.type === "a"
? pointer.relay
? ["a", pointer.value, pointer.relay]
? pointerRelay
? ["a", pointer.value, pointerRelay]
: ["a", pointer.value]
: pointer.relay
? ["e", pointer.value, pointer.relay]
: pointerRelay
? ["e", pointer.value, pointerRelay]
: ["e", pointer.value];
const normalizedPointer = {
type: pointer.type,
value: pointer.value,
relay: pointerRelay || null,
};
const explicitTargetAuthor =
typeof options.targetAuthorPubkey === "string" && options.targetAuthorPubkey.trim()
? options.targetAuthorPubkey.trim()
: typeof options.authorPubkey === "string" && options.authorPubkey.trim()
? options.authorPubkey.trim()
: "";
let content = "+";
if (typeof options.content === "string") {
content = options.content;
@@ -82,6 +105,8 @@ export async function publishVideoReaction(
created_at: createdAt,
pointerValue: pointer.value,
pointerTag,
targetPointer: normalizedPointer,
targetAuthorPubkey: explicitTargetAuthor,
additionalTags,
content,
});
+89
View File
@@ -689,6 +689,8 @@ export function buildReactionEvent({
pointerValue,
pointerTag,
pointerTags = [],
targetPointer = null,
targetAuthorPubkey = "",
additionalTags = [],
content = "",
}) {
@@ -704,6 +706,93 @@ export function buildReactionEvent({
tags.push(tag);
});
const resolvePointerDetails = (pointerCandidate) => {
if (!pointerCandidate) {
return null;
}
if (Array.isArray(pointerCandidate) && pointerCandidate.length >= 2) {
const [type, value, relay] = pointerCandidate;
const normalizedType = type === "a" ? "a" : type === "e" ? "e" : "";
const normalizedValue = typeof value === "string" ? value.trim() : "";
if (!normalizedType || !normalizedValue) {
return null;
}
const normalizedRelay =
typeof relay === "string" && relay.trim() ? relay.trim() : "";
return { type: normalizedType, value: normalizedValue, relay: normalizedRelay };
}
if (pointerCandidate && typeof pointerCandidate === "object") {
const { type, value, relay } = pointerCandidate;
const normalizedType = type === "a" ? "a" : type === "e" ? "e" : "";
const normalizedValue = typeof value === "string" ? value.trim() : "";
if (!normalizedType || !normalizedValue) {
return null;
}
const normalizedRelay =
typeof relay === "string" && relay.trim() ? relay.trim() : "";
return { type: normalizedType, value: normalizedValue, relay: normalizedRelay };
}
return null;
};
const pointerDetails = (() => {
const explicitPointer = resolvePointerDetails(targetPointer);
if (explicitPointer) {
return explicitPointer;
}
const pointerTagEntry = normalizedPointerTags.find(
(tag) => Array.isArray(tag) && tag.length >= 2 && (tag[0] === "a" || tag[0] === "e")
);
if (!pointerTagEntry) {
return null;
}
return resolvePointerDetails(pointerTagEntry);
})();
const resolvedRelay = pointerDetails?.relay || "";
const normalizedAuthorPubkey = (() => {
if (typeof targetAuthorPubkey === "string" && targetAuthorPubkey.trim()) {
return targetAuthorPubkey.trim();
}
if (pointerDetails?.type === "a" && pointerDetails.value) {
const segments = pointerDetails.value.split(":");
if (segments.length >= 2) {
const candidate = segments[1]?.trim();
if (candidate) {
return candidate;
}
}
}
return "";
})();
if (normalizedAuthorPubkey) {
const existingAuthorTag = tags.find(
(tag) => Array.isArray(tag) && tag[0] === "p" && tag[1] === normalizedAuthorPubkey
);
if (!existingAuthorTag) {
if (resolvedRelay) {
tags.push(["p", normalizedAuthorPubkey, resolvedRelay]);
} else {
tags.push(["p", normalizedAuthorPubkey]);
}
} else if (resolvedRelay) {
if (existingAuthorTag.length < 3) {
existingAuthorTag.push(resolvedRelay);
} else if (existingAuthorTag[2] !== resolvedRelay) {
existingAuthorTag[2] = resolvedRelay;
}
}
}
if (Array.isArray(additionalTags)) {
additionalTags.forEach((tag) => {
if (Array.isArray(tag) && tag.length >= 2) {
+80 -1
View File
@@ -4,7 +4,86 @@ import { devLogger, userLogger } from "./utils/logger.js";
export const reactionCounter = {
async publish(pointer, options = {}) {
try {
const result = await publishVideoReaction(pointer, options);
const publishOptions = { ...options };
const pointerRelayFromPointer = (() => {
if (Array.isArray(pointer) && pointer.length >= 3) {
const relay = pointer[2];
if (typeof relay === "string" && relay.trim()) {
return relay.trim();
}
}
if (pointer && typeof pointer === "object") {
const relay = pointer.relay;
if (typeof relay === "string" && relay.trim()) {
return relay.trim();
}
}
return "";
})();
const pointerRelay =
typeof publishOptions.pointerRelay === "string" && publishOptions.pointerRelay.trim()
? publishOptions.pointerRelay.trim()
: pointerRelayFromPointer;
if (pointerRelay && !publishOptions.pointerRelay) {
publishOptions.pointerRelay = pointerRelay;
}
const enrichedPointer = (() => {
if (!pointerRelay) {
return pointer;
}
if (Array.isArray(pointer) && pointer.length >= 2) {
const [type, value] = pointer;
return [type, value, pointerRelay];
}
if (pointer && typeof pointer === "object") {
return { ...pointer, relay: pointerRelay };
}
return pointer;
})();
const explicitAuthor =
typeof publishOptions.targetAuthorPubkey === "string" &&
publishOptions.targetAuthorPubkey.trim()
? publishOptions.targetAuthorPubkey.trim()
: "";
if (!explicitAuthor) {
const fallbackAuthor = (() => {
if (
typeof publishOptions.authorPubkey === "string" &&
publishOptions.authorPubkey.trim()
) {
return publishOptions.authorPubkey.trim();
}
if (
typeof publishOptions.currentVideoPubkey === "string" &&
publishOptions.currentVideoPubkey.trim()
) {
return publishOptions.currentVideoPubkey.trim();
}
if (
publishOptions.video &&
typeof publishOptions.video.pubkey === "string" &&
publishOptions.video.pubkey.trim()
) {
return publishOptions.video.pubkey.trim();
}
return "";
})();
if (fallbackAuthor) {
publishOptions.targetAuthorPubkey = fallbackAuthor;
}
}
const result = await publishVideoReaction(enrichedPointer, publishOptions);
if (!result || !result.ok) {
userLogger.warn(
"[reactionCounter] Reaction publish rejected by relays:",
+39
View File
@@ -0,0 +1,39 @@
import test from "node:test";
import assert from "node:assert/strict";
import { buildReactionEvent } from "../js/nostrEventSchemas.js";
test("buildReactionEvent includes pointer and author tags when pubkey provided", () => {
const event = buildReactionEvent({
pubkey: "viewer",
created_at: 1700000000,
pointerValue: "event-id",
pointerTag: ["e", "event-id", "wss://relay.example"],
targetPointer: { type: "e", value: "event-id", relay: "wss://relay.example" },
targetAuthorPubkey: "author-pubkey",
content: "+",
});
const pointerTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "e");
assert.deepEqual(pointerTags, [["e", "event-id", "wss://relay.example"]]);
const authorTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "p");
assert.deepEqual(authorTags, [["p", "author-pubkey", "wss://relay.example"]]);
});
test("buildReactionEvent derives author pubkey from address pointer", () => {
const event = buildReactionEvent({
pubkey: "viewer",
created_at: 1700000001,
pointerValue: "30078:deadbeefcafebabe:clip-1",
pointerTag: ["a", "30078:deadbeefcafebabe:clip-1"],
targetPointer: { type: "a", value: "30078:deadbeefcafebabe:clip-1" },
content: "+",
});
const addressTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "a");
assert.deepEqual(addressTags, [["a", "30078:deadbeefcafebabe:clip-1"]]);
const authorTags = event.tags.filter((tag) => Array.isArray(tag) && tag[0] === "p");
assert.deepEqual(authorTags, [["p", "deadbeefcafebabe"]]);
});