commit 0d3e5cb6ec51d6f9dde82c8dd131d658a407f218
parent 32abad61ff9d20265bc932672c05e892490c087b
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sun, 17 Nov 2024 07:55:06 +0000
utils: add/edit utils
Diffstat:
11 files changed, 188 insertions(+), 25 deletions(-)
diff --git a/utils/package.json b/utils/package.json
@@ -18,10 +18,13 @@
"access": "public"
},
"dependencies": {
+ "@noble/curves": "^1.6.0",
"@noble/hashes": "^1.4.0",
"@nostr-dev-kit/ndk": "^2.10.0",
"convert": "^5.5.1",
"ngeohash": "0.6.3",
+ "nostr-geotags": "^0.7.1",
+ "nostr-tools": "^2.7.2",
"uuid": "^10.0.0",
"zod": "^3.23.8"
}
diff --git a/utils/src/error.ts b/utils/src/error.ts
@@ -1,11 +1,19 @@
import type { ErrorMessage, ErrorResponse } from "./types";
-export const handle_error = (e: unknown, append?: string): ErrorResponse => {
+export const handle_error = (e: unknown, append?: string): ErrorMessage<string> => {
const msg = (e as Error).message ? (e as Error).message : String(e);
- const error = `${msg}${append ? ` ${append}` : ``}`;
- return { error };
+ const err = `${msg}${append ? ` ${append}` : ``}`;
+ return { err };
};
export const err_msg = <T extends string>(err: T): ErrorMessage<T> => {
return { err };
};
+
+export const err_res = <T extends object>(error: T): ErrorResponse<T> => {
+ return { error };
+};
+
+export const err_system = (message: string): boolean => {
+ return message.split(` `).length > 1
+};
+\ No newline at end of file
diff --git a/utils/src/file.ts b/utils/src/file.ts
@@ -30,6 +30,7 @@ export const parse_file_path = (file_path: string): FilePath | undefined => {
const [file_name, mime_type] = file_path_file.split(`.`);
if (!file_name || !mime_type) return undefined;
return {
+ file_path,
file_name,
mime_type
};
diff --git a/utils/src/format.ts b/utils/src/format.ts
@@ -0,0 +1,4 @@
+export const fmt_plural_agreement = (num: number = 0, val_s: string, val_pl: string): string => {
+ if (num === 1) return val_s;
+ return val_pl;
+};
+\ No newline at end of file
diff --git a/utils/src/index.ts b/utils/src/index.ts
@@ -2,6 +2,7 @@
export * from "./currency"
export * from "./error"
export * from "./file"
+export * from "./format"
export * from "./geolocation"
export * from "./hash"
export * from "./list"
@@ -9,6 +10,7 @@ export * from "./math"
export * from "./nostr/lib"
export * from "./nostr/ndk"
export * from "./nostr/types"
+export * from "./object"
export * from "./regex"
export * from "./time"
export * from "./trade"
diff --git a/utils/src/nostr/geotags.ts b/utils/src/nostr/geotags.ts
@@ -0,0 +1,21 @@
+import ngeotags, { type GeoTags, type InputData, type Options } from 'nostr-geotags';
+import { NostrTagLocation } from './types';
+
+const options: Options = {
+ geohash: true,
+ gps: true,
+ city: true,
+ iso31662: true,
+};
+
+export const fmt_tag_geotags = (opts: NostrTagLocation): GeoTags[] => {
+ const data: InputData = {
+ lat: opts.lat,
+ lon: opts.lng,
+ city: opts.city,
+ regionName: opts.region,
+ countryName: opts.country,
+ countryCode: opts.country_code
+ };
+ return ngeotags(data, options)
+};
+\ No newline at end of file
diff --git a/utils/src/nostr/lib.ts b/utils/src/nostr/lib.ts
@@ -1,4 +1,9 @@
-import type { NostrRelayInformationDocument, NostrRelayInformationDocumentFormFields } from "./types";
+import { schnorr } from '@noble/curves/secp256k1';
+import { hexToBytes } from '@noble/hashes/utils';
+import { type EventTemplate, finalizeEvent, getEventHash, type NostrEvent } from "nostr-tools";
+import { uuidv4 } from '../uuid';
+import { fmt_tag_geotags } from './geotags';
+import type { NostrRelayInformationDocument, NostrRelayInformationDocumentFormFields, NostrTagListing, NostrTagLocation, NostrTagPrice, NostrTagQuantity } from "./types";
export const parse_nostr_relay_information_document = (data: any): NostrRelayInformationDocument | undefined => {
const obj = JSON.parse(data);
@@ -36,31 +41,106 @@ export const parse_nostr_relay_information_document_fields = (data: any): NostrR
return result;
};
+
+export const fmt_tag_price = (opts: NostrTagPrice): string[] => {
+ const tag = [`price`, opts.amt, opts.currency, opts.qty_amt, opts.qty_unit];
+ return tag;
+};
+
+
+export const fmt_tag_quantity = (opts: NostrTagQuantity): string[] => {
+ const tag = [`quantity`, opts.amt, opts.unit];
+ if (opts.label) tag.push(opts.label);
+ return tag;
+};
+
+
+export const fmt_tag_location = (opts: NostrTagLocation): string[] => {
+ const tag = [`location`];
+ if (opts.city) tag.push(opts.city);
+ if (opts.region_code) tag.push(opts.region_code);
+ if (opts.country_code) tag.push(opts.country_code);
+ return tag;
+};
+
+type NostrTagMediaUpload = {
+ url: string;
+ size?: {
+ w: number;
+ h: number;
+ };
+};
+export const fmt_tag_image = (opts: NostrTagMediaUpload): string[] => {
+ const tag = ["image", opts.url];
+ if (opts.size) tag.push(`${opts.size.w}x${opts.size.h}`)
+ return tag;
+};
+
export const fmt_tags_basis_nip99 = async (opts: {
d_tag: string;
- title: string;
- image?: {
- url: string;
- size: string;
- };
- summary?: string;
- location?: string;
- price?: {
- amt: string;
- currency: string;
- };
+ listing: NostrTagListing;
+ quantity: NostrTagQuantity;
+ price: NostrTagPrice;
+ location: NostrTagLocation;
+ images?: NostrTagMediaUpload[];
}): Promise<string[][] | undefined> => {
try {
const tags: string[][] = [
- ["d", opts.d_tag],
- ["title", opts.title],
+ [`d`, opts.d_tag],
];
- if (opts.image) tags.push(["image", opts.image.url, opts.image.size],)
- if (opts.summary) tags.push(["summary", opts.summary])
- if (opts.location) tags.push(["location", opts.location])
- if (opts.price) tags.push(["price", opts.price.amt, opts.price.currency])
+ for (const [k, v] of Object.entries(opts.listing)) if (v) tags.push([k, v]);
+ tags.push(fmt_tag_quantity(opts.quantity));
+ tags.push(fmt_tag_price(opts.price));
+ tags.push(fmt_tag_location(opts.location));
+ if (opts.images) for (const image of opts.images) tags.push(fmt_tag_image(image));
+ tags.push(...fmt_tag_geotags(opts.location))
return tags;
} catch (e) {
console.log(`(error) fmt_tags_nip99 `, e);
}
+};
+
+export const nostr_event_sign = (opts: {
+ secret_key: string;
+ event: EventTemplate;
+}): NostrEvent => {
+ return finalizeEvent(opts.event, hexToBytes(opts.secret_key))
+};
+
+export const nostr_event_sign_attest = (secret_key: string): NostrEvent => {
+ return nostr_event_sign({
+ secret_key,
+ event: {
+ kind: 1,
+ created_at: Math.floor(Date.now() / 1000),
+ tags: [],
+ content: uuidv4(),
+ },
+ });
+};
+
+
+export const nostr_event_verify = async (event: NostrEvent): Promise<boolean> => {
+ try {
+ const hash = getEventHash(event);
+ console.log(`hash `, hash)
+ if (hash !== event.id) return false
+ const valid = schnorr.verify(event.sig, hash, event.pubkey);
+ return valid;
+ } catch {
+ return false;
+ }
+};
+
+export const nostr_event_verify_serialized = async (event_serialized: string): Promise<boolean> => {
+ try {
+ const event = JSON.parse(event_serialized);
+ const hash = getEventHash(event);
+ console.log(`hash `, hash)
+ if (hash !== event.id) return false
+ const valid = schnorr.verify(event.sig, hash, event.pubkey);
+ return valid;
+ } catch {
+ return false;
+ }
};
\ No newline at end of file
diff --git a/utils/src/nostr/types.ts b/utils/src/nostr/types.ts
@@ -12,3 +12,37 @@ export type NostrRelayInformationDocument = {
}
export type NostrRelayInformationDocumentFormFields = { [K in keyof NostrRelayInformationDocument]: string; };
+
+export type NostrTagListing = {
+ title: string;
+ summary?: string;
+ process?: string;
+ lot?: string;
+ location?: string;
+ profile?: string;
+ year?: string;
+};
+
+export type NostrTagPrice = {
+ amt: string;
+ currency: string;
+ qty_amt: string;
+ qty_unit: string;
+};
+
+export type NostrTagQuantity = {
+ amt: string;
+ unit: string;
+ label?: string;
+};
+
+export type NostrTagLocation = {
+ city?: string;
+ region?: string;
+ region_code?: string;
+ country?: string;
+ country_code?: string;
+ lat: number;
+ lng: number;
+ geohash: string;
+};
+\ No newline at end of file
diff --git a/utils/src/object.ts b/utils/src/object.ts
@@ -0,0 +1,3 @@
+export const obj_en = <KeyType extends string, ValType>(object: Record<string, ValType>, parse_function: (key: string) => KeyType): [KeyType, ValType][] => {
+ return Object.entries(object).map<[KeyType, ValType]>(([k, v]) => [parse_function(k), v])
+};
+\ No newline at end of file
diff --git a/utils/src/regex.ts b/utils/src/regex.ts
@@ -1,4 +1,6 @@
export const regex = {
+ description: /^(?:\S+(?:\s+\S+)*)$/,
+ description_ch: /[^a-zA-Z0-9.,!?;:'"(){}[]\s\u0600-\u06FF\u0900-\u097F\u0400-\u04FF\u0500-\u052F\u1F00-\u1FFF\u4E00-\u9FFF\uAC00-\uD7AF\u3040-\u309F\u30A0-\u30FF ]+/,
nbsp: /[\u00A0]/g,
nbsp_rp: /[\u00A0]+/g,
rtlm: /[\u200F]/g,
@@ -10,7 +12,8 @@ export const regex = {
alpha_ch: /[a-zA-Z ]$/,
num: /^[0-9]+$/,
alphanum: /[a-zA-Z0-9., ]$/,
- alphanum_ch: /[a-zA-Z0-9., ]/,
+ alphanum_ch: /[a-zA-Z0-9.,\s\u0600-\u06FF\u0900-\u097F\u0400-\u04FF\u0500-\u052F\u1F00-\u1FFF\u4E00-\u9FFF\uAC00-\uD7AF\u3040-\u309F\u30A0-\u30FF ]+/,
+ ///[a-zA-Z0-9., ]/,
price: /^\d+(\.\d+)?$/,
price_ch: /[0-9.]$/,
profile_name: /^[a-zA-Z0-9._]{3,30}$/,
diff --git a/utils/src/types.ts b/utils/src/types.ts
@@ -1,6 +1,6 @@
import type { GeolocationCoordinatesPoint } from "./geolocation";
-export type ErrorResponse = { error: string; };
+export type ErrorResponse<T extends object> = { error: T; };
export type ErrorMessage<T extends string> = { err: T };
export type ResultId = { id: string; };
@@ -20,4 +20,4 @@ export type NumberTuple = [number, number];
export type FileBytesFormat = `kb` | `mb` | `gb`;
export type FileMimeType = string;
-export type FilePath = { file_name: string; mime_type: FileMimeType; }
-\ No newline at end of file
+export type FilePath = { file_path: string; file_name: string; mime_type: FileMimeType; }