commit 4001dfc948a57aa6b5acb900013506ea805364bc
parent f9061bab5631699fff65bf0a2d8a9d2328dc10a5
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Thu, 15 May 2025 01:19:37 +0000
utils-nostr: add nip-25 reactions events, refactor tags utils, add nip-19 utils
Diffstat:
5 files changed, 80 insertions(+), 17 deletions(-)
diff --git a/utils-nostr/package.json b/utils-nostr/package.json
@@ -29,9 +29,9 @@
"@noble/curves": "^1.6.0",
"@noble/hashes": "^1.4.0",
"@nostr-dev-kit/ndk": "^2.11.0",
- "@radroots/radroots-common-bindings": "workspace:*",
- "@radroots/util": "workspace:*",
- "nostr-geotags": "workspace:*",
+ "@radroots/radroots-common-bindings": "*",
+ "@radroots/util": "*",
+ "nostr-geotags": "*",
"nostr-tools": "^2.10.4",
"uuid": "^10.0.0"
}
diff --git a/utils-nostr/src/lib/keys.ts b/utils-nostr/src/lib/keys.ts
@@ -15,6 +15,18 @@ export const lib_nostr_key_generate = (): string => {
return lib_nostr_get_key_hex(bytes);
};
+export const lib_nostr_npub_encode = (public_key_hex?: string): string | undefined => {
+ if (!public_key_hex) return undefined;
+ const npub = nip19.npubEncode(public_key_hex)
+ return npub;
+};
+
+export const lib_nostr_npub_decode = (npub?: string): string | undefined => {
+ if (!npub) return undefined;
+ const { type, data } = nip19.decode(npub)
+ if (type === `npub` && data) return data
+};
+
export const lib_nostr_nsec_encode = (secret_key_hex?: string): string | undefined => {
if (!secret_key_hex) return undefined;
const bytes = lib_nostr_get_key_bytes(secret_key_hex);
@@ -28,6 +40,17 @@ export const lib_nostr_nsec_decode = (nsec?: string): string | undefined => {
return undefined;
};
+export const lib_nostr_nprofile_encode = (public_key_hex: string, relays: string[]): string | undefined => {
+ if (!public_key_hex || !relays.length) return undefined;
+ const nprofile = nip19.nprofileEncode({ pubkey: public_key_hex,relays })
+ return nprofile;
+};
+
+export const lib_nostr_nprofile_decode = (nprofile?: string): nip19.ProfilePointer | undefined => {
+ if (!nprofile) return undefined;
+ const { type, data } = nip19.decode(nprofile)
+ if (type === `nprofile` && data) return data
+};
export const lib_nostr_public_key = (secret_key_hex: string): string => {
const bytes = lib_nostr_get_key_bytes(secret_key_hex);
diff --git a/utils-nostr/src/lib/ndk.ts b/utils-nostr/src/lib/ndk.ts
@@ -1,4 +1,4 @@
-import { INostrClassified, INostrFollow, INostrJobRequest, NostrEventTags, tags_classified, tags_follow_list, tags_job_request, time_now_ms, type INostrMetadata } from '$root';
+import { INostrClassified, INostrFollow, INostrJobRequest, INostrReaction, NostrEventTags, tags_classified, tags_follow_list, tags_job_request, tags_reaction, time_now_ms, type INostrMetadata } from '$root';
import NDK, { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKUser } from '@nostr-dev-kit/ndk';
export type NDKEventFigure<T extends object> = {
@@ -33,7 +33,6 @@ export const ndk_event = async (opts: NDKEventFigure<{
}>): Promise<NDKEvent | undefined> => {
try {
const { $ndk: ndk, $ndk_user: ndk_user, basis } = opts;
- console.log(JSON.stringify(basis, null, 4), `basis`)
const time_now = time_now_ms();
const tags: NostrEventTags = [
['published_at', time_now.toString()],
@@ -110,3 +109,18 @@ export const ndk_event_job_request = async (opts: NDKEventFigure<{
},
});
};
+
+export const ndk_event_reaction = async (opts: NDKEventFigure<{
+ data: INostrReaction;
+}>): Promise<NDKEvent | undefined> => {
+ const { $ndk, $ndk_user, data } = opts;
+ return await ndk_event({
+ $ndk,
+ $ndk_user,
+ basis: {
+ kind: NDKKind.Reaction,
+ content: data.content,
+ tags: tags_reaction(data)
+ },
+ });
+};
diff --git a/utils-nostr/src/lib/tags.ts b/utils-nostr/src/lib/tags.ts
@@ -1,5 +1,5 @@
-import { INostrClassified, INostrJobRequest, NostrEventTagClient, NostrEventTagLocation, NostrEventTagMediaUpload, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, type INostrFollow, type NostrEventTag, type NostrEventTags } from "$root";
-import { ngeotags, type InputData as NostrGeotagsInputData } from "nostr-geotags";
+import { INostrClassified, INostrJobRequest, INostrReaction, NostrEventTagClient, NostrEventTagLocation, NostrEventTagMediaUpload, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, type INostrFollow, type NostrEventTag, type NostrEventTags } from "$root";
+import ngeotags, { type InputData as NostrGeotagsInputData } from "nostr-geotags";
export const tag_client = (opts: NostrEventTagClient, d_tag?: string): NostrEventTag => {
const tag = [`client`, opts.name];
@@ -44,17 +44,20 @@ export const tag_classified_image = (opts: NostrEventTagMediaUpload): NostrEvent
};
export const tag_classified_location = (opts: NostrEventTagLocation): NostrEventTag => {
- const tag = [`location`];
+ if (!opts.primary) return [];
+ const tag = [`location`, opts.primary];
if (opts.city) tag.push(opts.city);
- if (opts.region_code && !isNaN(parseInt(opts.region_code))) tag.push(opts.region_code);
- else if (opts.region) tag.push(opts.region); //@todo
- if (opts.country_code) tag.push(opts.country_code);
+ if (opts.region) tag.push(opts.region);
+ if (opts.country) tag.push(opts.country);
return tag;
};
export const tags_classified_location_geotags = (opts: NostrEventTagLocation): NostrEventTags => {
- const { lat, lng: lon, city, region: regionName, country: countryName, country_code: countryCode } = opts;
- return ngeotags({ lat, lon, city, regionName, countryName, countryCode } satisfies NostrGeotagsInputData, { geohash: true, gps: true, city: true, iso31662: true });
+ const { lat, lng: lon, city, region: regionName, country } = opts;
+ const country_raw = country || ``;
+ const countryCode = country_raw && country_raw?.length <= 3 ? country_raw : undefined;
+ const countryName = country_raw && country_raw?.length > 3 ? country_raw : undefined;
+ return ngeotags({ lat, lon, city, regionName, countryCode, countryName } satisfies NostrGeotagsInputData, { geohash: true, gps: true, city: true, iso31662: true });
};
export const tags_classified = (opts: INostrClassified): NostrEventTags => {
@@ -73,7 +76,7 @@ export const tags_classified = (opts: INostrClassified): NostrEventTags => {
}
if (opts.location) {
tags.push(tag_classified_location(opts.location));
- //tags.push(...tags_classified_location_geotags(opts.location));
+ tags.push(...tags_classified_location_geotags(opts.location));
}
if (opts.images) for (const image_tags of opts.images) tags.push(tag_classified_image(image_tags));
return tags;
@@ -97,3 +100,16 @@ export const tags_job_request = (opts: INostrJobRequest): NostrEventTags => {
tags.push(...(opts.tags || []))
return tags;
};
+
+export const tags_reaction = (opts: INostrReaction): NostrEventTags => {
+ const { ref_event } = opts;
+ const ref_kind = ref_event.kind.toString();
+ const ref_author = ref_event.author;
+ const tags: NostrEventTags = [
+ [`e`, ref_event.id, ...ref_event.relays || ``],
+ [`p`, ref_author],
+ [`k`, ref_kind],
+ ];
+ if (ref_event.d_tag) tags.push([`a`, `${ref_kind}:${ref_author}:${ref_event.d_tag}`, ...ref_event.relays || ``])
+ return tags;
+};
diff --git a/utils-nostr/src/lib/types.ts b/utils-nostr/src/lib/types.ts
@@ -117,14 +117,12 @@ export type NostrEventTagListing = {
};
export type NostrEventTagLocation = {
+ primary: string;
city?: string;
region?: string;
- region_code?: string;
country?: string;
- country_code?: string;
lat?: number;
lng?: number;
- geohash: string;
};
export type NostrEventTagMediaUpload = {
@@ -140,3 +138,14 @@ export type NostrEventTagClient = {
pubkey: string;
relay: string;
};
+
+export type INostrReaction = {
+ ref_event: {
+ id: string;
+ kind: number;
+ author: string;
+ relays?: string[];
+ d_tag?: string;
+ },
+ content: string;
+};
+\ No newline at end of file