commit b724635e74628c6c0d0a9a1f262b1257b7d3a4c8
parent c5b88e683eedfd9aa8de78fec8be1d8c317324ec
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sat, 26 Apr 2025 23:45:28 +0000
utils-nostr: add `@radroots/radroots-common-bindings` and edit classified event tags utils
Diffstat:
7 files changed, 83 insertions(+), 72 deletions(-)
diff --git a/utils-nostr/package.json b/utils-nostr/package.json
@@ -29,6 +29,7 @@
"@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:*",
"nostr-tools": "^2.10.4",
diff --git a/utils-nostr/src/lib/tags.ts b/utils-nostr/src/lib/tags.ts
@@ -1,4 +1,4 @@
-import { INostrClassified, INostrJobRequest, NostrEventTagClient, NostrEventTagLocation, NostrEventTagMediaUpload, NostrEventTagPrice, NostrEventTagPriceTier, NostrEventTagQuantity, type INostrFollow, type NostrEventTag, type NostrEventTags } from "$root";
+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";
export const tag_client = (opts: NostrEventTagClient, d_tag?: string): NostrEventTag => {
@@ -20,16 +20,21 @@ export const tags_follow_list = (list: INostrFollow[]): NostrEventTags => {
export const tag_classified_quantity = (opts: NostrEventTagQuantity): NostrEventTag => {
const tag = [`quantity`, opts.amt, opts.unit];
if (opts.label) tag.push(opts.label);
- return tag;
+ return tag.map(i => i.toLowerCase());
};
-export const tag_classified_price_tier = (tier: NostrEventTagPriceTier, price_key: string): NostrEventTag => {
- const tag = [`price-tier`, tier.type, tier.value, price_key, tier.qty_min.toString()];
- return tag;
+
+export const tag_classified_price_discount = (discount: NostrEventTagPriceDiscount): NostrEventTag => {
+ const tag = [`price-discount-${Object.keys(discount)[0]}`];
+ if (`mass` in discount) tag.push(...Object.values(discount.mass));
+ else if (`quantity` in discount) tag.push(...Object.values(discount.quantity));
+ else if (`subtotal` in discount) tag.push(...Object.values(discount.subtotal));
+ else if (`total` in discount) tag.push(...Object.values(discount.total));
+ return tag.map(i => i.toLowerCase());
};
-export const tag_classified_price = (tag_price: NostrEventTagPrice, tag_qty: NostrEventTagQuantity, quantity_key: string): NostrEventTag => {
- const tag = [`price`, tag_price.amt, tag_price.currency, tag_qty.amt, tag_qty.unit, quantity_key];
- return tag;
+export const tag_classified_price = (price: NostrEventTagPrice): NostrEventTag => {
+ const tag = [`price`, price.amt, price.currency, price.qty_amt, price.qty_unit, price.qty_key];
+ return tag.map(i => i.toLowerCase());
};
export const tag_classified_image = (opts: NostrEventTagMediaUpload): NostrEventTag => {
@@ -48,43 +53,29 @@ export const tag_classified_location = (opts: NostrEventTagLocation): NostrEvent
};
export const tags_classified_location_geotags = (opts: NostrEventTagLocation): NostrEventTags => {
- return ngeotags(
- {
- lat: opts.lat,
- lon: opts.lng,
- city: opts.city,
- regionName: opts.region,
- countryName: opts.country,
- countryCode: opts.country_code
- } satisfies NostrGeotagsInputData,
- {
- geohash: true,
- gps: true,
- city: true,
- iso31662: true,
- });
+ 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 });
};
-
export const tags_classified = (opts: INostrClassified): NostrEventTags => {
- const { d_tag, listing, quantities, location } = opts;
+ const { d_tag, listing, quantities, prices } = opts;
const tags: NostrEventTags = [[`d`, d_tag]];
if (opts.client) tags.push(tag_client(opts.client, opts.d_tag));
for (const [k, v] of Object.entries(listing)) if (v) tags.push([k, v]);
for (const quantity of quantities) {
- const quantity_key = `${quantity.amt}-${quantity.unit}${quantity.label ? `-${quantity.label}` : ``}`.toLowerCase();
tags.push(tag_classified_quantity(quantity));
- for (const price of quantity.prices) {
- const price_key = `${price.amt}-${price.currency}-${quantity.amt}-${quantity.unit}`.toLowerCase();
- tags.push(tag_classified_price(price, quantity, quantity_key));
- for (const tier of price.tiers || []) {
- tags.push(tag_classified_price_tier(tier, price_key));
- }
- }
}
- tags.push(tag_classified_location(location));
+ for (const price of prices) {
+ tags.push(tag_classified_price(price));
+ }
+ for (const discount of opts.discounts || []) {
+ tags.push(tag_classified_price_discount(discount));
+ }
+ if (opts.location) {
+ tags.push(tag_classified_location(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));
- tags.push(...tags_classified_location_geotags(location));
return tags;
};
diff --git a/utils-nostr/src/lib/types.ts b/utils-nostr/src/lib/types.ts
@@ -1,3 +1,4 @@
+import { ListingOrder } from "@radroots/radroots-common-bindings";
import { type EventTemplate as NostrToolsEventTemplate } from "nostr-tools";
export type INostrMetadata = {
@@ -23,51 +24,64 @@ export type NostrEventTagQuantity = {
amt: string;
unit: string;
label?: string;
- prices: NostrEventTagPrice[];
};
-export type NostrEventTagPriceTier = {
- type: string;
- value: string;
- qty_min: number;
-}
+export type NostrEventTagPriceDiscount = (
+ {
+ quantity: {
+ ref_quantity: string;
+ threshold: string;
+ value: string;
+ currency: string;
+ }
+ } |
+ {
+ mass: {
+ unit: string;
+ threshold: string;
+ threshold_unit: string;
+ value: string;
+ currency: string;
+ }
+ } |
+ {
+ subtotal: {
+ threshold: string;
+ currency: string;
+ value: string;
+ measure: string;
+ }
+ } |
+ {
+ total: {
+ total_min: string;
+ value: string;
+ measure: string;
+ }
+ }
+);
+
export type NostrEventTagPrice = {
amt: string;
currency: string;
- tiers?: NostrEventTagPriceTier[];
+ qty_amt: string;
+ qty_unit: string;
+ qty_key: string;
};
export type INostrClassified = {
d_tag: string;
listing: NostrEventTagListing;
quantities: NostrEventTagQuantity[];
- location: NostrEventTagLocation;
+ prices: NostrEventTagPrice[];
+ discounts?: NostrEventTagPriceDiscount[];
+ location?: NostrEventTagLocation;
images?: NostrEventTagMediaUpload[];
client?: NostrEventTagClient;
};
export type NostrJobRequestMassUnit = 'g' | 'kg' | 'lb';
-export type INostrJobRequestOrderQuantity = {
- amount: number;
- unit: string;
- count: number;
- mass_g: number;
- label: string;
-};
-
-export type INostrJobRequestOrderPrice = {
- amount: number;
- currency: string;
- quantity_amount: number;
- quantity_unit: NostrJobRequestMassUnit;
-};
-
-export type INostrJobRequestOrder = {
- price: INostrJobRequestOrderPrice;
- quantity: INostrJobRequestOrderQuantity;
-}
-
export type INostrJobRequestInput = {
tags?: string[];
} & ({
@@ -75,7 +89,7 @@ export type INostrJobRequestInput = {
id: string;
relay: string;
marker?: ({
- order: INostrJobRequestOrder;
+ order: ListingOrder;
});
}
})
@@ -108,8 +122,8 @@ export type NostrEventTagLocation = {
region_code?: string;
country?: string;
country_code?: string;
- lat: number;
- lng: number;
+ lat?: number;
+ lng?: number;
geohash: string;
};
diff --git a/utils-nostr/src/services/events/lib.ts b/utils-nostr/src/services/events/lib.ts
@@ -1,6 +1,5 @@
-import { INostrClassified, INostrEventEventSign, INostrEventService, INostrEventServiceEventResolve, INostrEventServiceNeventEncode, INostrFollow, INostrMetadata, lib_nostr_event_sign, lib_nostr_event_sign_attest, lib_nostr_event_verify, lib_nostr_event_verify_serialized, lib_nostr_nevent_encode, ndk_event, ndk_event_classified, ndk_event_follows } from "$root";
+import { err_msg, ErrorMessage, INostrClassified, INostrEventEventSign, INostrEventService, INostrEventServiceEventResolve, INostrEventServiceNeventEncode, INostrFollow, INostrMetadata, lib_nostr_event_sign, lib_nostr_event_sign_attest, lib_nostr_event_verify, lib_nostr_event_verify_serialized, lib_nostr_nevent_encode, ndk_event, ndk_event_classified, ndk_event_follows } from "$root";
import NDK, { NDKKind, NDKUser, type NDKEvent } from "@nostr-dev-kit/ndk";
-import { err_msg, ErrorMessage } from "@radroots/util";
import { type NostrEvent as NostrToolsEvent } from "nostr-tools";
export class NostrEventService implements INostrEventService {
diff --git a/utils-nostr/src/services/events/types.ts b/utils-nostr/src/services/events/types.ts
@@ -1,6 +1,5 @@
-import type { INostrClassified, INostrEventEventSign, INostrFollow, INostrMetadata } from "$root";
+import type { INostrClassified, INostrEventEventSign, INostrFollow, INostrMetadata, ResolveError } from "$root";
import NDK, { NDKEvent } from "@nostr-dev-kit/ndk";
-import { type ResolveError } from "@radroots/util";
import { type NostrEvent as NostrToolsEvent } from "nostr-tools";
export type INostrEventServiceNeventEncode = {
diff --git a/utils-nostr/src/types.ts b/utils-nostr/src/types.ts
@@ -1,2 +1,4 @@
export type NostrEventTag = string[];
export type NostrEventTags = NostrEventTag[];
+export type ErrorMessage<T extends string> = { err: T };
+export type ResolveError<T> = T | ErrorMessage<string>;
diff --git a/utils-nostr/src/util.ts b/utils-nostr/src/util.ts
@@ -1,5 +1,10 @@
+import { type ErrorMessage } from "$root";
import { v4 } from "uuid";
export const time_now_ms = (): number => Math.floor(new Date().getTime() / 1000);
-export const uuidv4 = (): string => v4();
-\ No newline at end of file
+export const uuidv4 = (): string => v4();
+
+export const err_msg = <T extends string>(err: T): ErrorMessage<T> => {
+ return { err };
+};
+\ No newline at end of file