web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

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:
Mutils-nostr/package.json | 6+++---
Mutils-nostr/src/lib/keys.ts | 23+++++++++++++++++++++++
Mutils-nostr/src/lib/ndk.ts | 18++++++++++++++++--
Mutils-nostr/src/lib/tags.ts | 34+++++++++++++++++++++++++---------
Mutils-nostr/src/lib/types.ts | 16+++++++++++++---
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