2023-01-06 14:36:13 +00:00
|
|
|
import * as secp from "@noble/secp256k1";
|
|
|
|
import { bech32 } from "bech32";
|
2023-01-20 11:11:50 +00:00
|
|
|
import { HexKey, RawEvent, TaggedRawEvent, u256 } from "Nostr";
|
|
|
|
import EventKind from "Nostr/EventKind";
|
2023-01-03 16:55:51 +00:00
|
|
|
|
2023-01-16 17:48:25 +00:00
|
|
|
export async function openFile(): Promise<File | undefined> {
|
2023-01-03 16:55:51 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let elm = document.createElement("input");
|
|
|
|
elm.type = "file";
|
2023-01-15 19:40:47 +00:00
|
|
|
elm.onchange = (e: Event) => {
|
|
|
|
let elm = e.target as HTMLInputElement;
|
|
|
|
if (elm.files) {
|
|
|
|
resolve(elm.files[0]);
|
2023-01-16 17:48:25 +00:00
|
|
|
} else {
|
|
|
|
resolve(undefined);
|
2023-01-15 19:40:47 +00:00
|
|
|
}
|
2023-01-03 16:55:51 +00:00
|
|
|
};
|
|
|
|
elm.click();
|
|
|
|
});
|
|
|
|
}
|
2023-01-06 14:36:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse bech32 ids
|
2023-01-06 14:45:30 +00:00
|
|
|
* https://github.com/nostr-protocol/nips/blob/master/19.md
|
2023-01-15 19:40:47 +00:00
|
|
|
* @param id bech32 id
|
2023-01-06 14:36:13 +00:00
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function parseId(id: string) {
|
2023-01-06 14:45:30 +00:00
|
|
|
const hrp = ["note", "npub", "nsec"];
|
2023-01-06 14:36:13 +00:00
|
|
|
try {
|
|
|
|
if (hrp.some(a => id.startsWith(a))) {
|
|
|
|
return bech32ToHex(id);
|
|
|
|
}
|
|
|
|
} catch (e) { }
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2023-01-15 19:40:47 +00:00
|
|
|
export function bech32ToHex(str: string) {
|
2023-01-06 14:36:13 +00:00
|
|
|
let nKey = bech32.decode(str);
|
|
|
|
let buff = bech32.fromWords(nKey.words);
|
|
|
|
return secp.utils.bytesToHex(Uint8Array.from(buff));
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:54:12 +00:00
|
|
|
/**
|
|
|
|
* Decode bech32 to string UTF-8
|
2023-01-15 19:40:47 +00:00
|
|
|
* @param str bech32 encoded string
|
2023-01-07 20:54:12 +00:00
|
|
|
* @returns
|
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function bech32ToText(str: string) {
|
2023-01-07 20:54:12 +00:00
|
|
|
let decoded = bech32.decode(str, 1000);
|
|
|
|
let buf = bech32.fromWords(decoded.words);
|
|
|
|
return new TextDecoder().decode(Uint8Array.from(buf));
|
|
|
|
}
|
|
|
|
|
2023-01-06 14:36:13 +00:00
|
|
|
/**
|
|
|
|
* Convert hex note id to bech32 link url
|
2023-01-15 19:40:47 +00:00
|
|
|
* @param hex
|
2023-01-06 14:36:13 +00:00
|
|
|
* @returns
|
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function eventLink(hex: u256) {
|
2023-01-09 16:18:34 +00:00
|
|
|
return `/e/${hexToBech32("note", hex)}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert hex to bech32
|
|
|
|
* @param {string} hex
|
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function hexToBech32(hrp: string, hex: string) {
|
2023-01-10 11:19:52 +00:00
|
|
|
if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 != 0) {
|
2023-01-10 11:41:57 +00:00
|
|
|
return "";
|
2023-01-10 11:19:52 +00:00
|
|
|
}
|
2023-01-15 19:40:47 +00:00
|
|
|
|
2023-01-14 11:57:36 +00:00
|
|
|
try {
|
|
|
|
let buf = secp.utils.hexToBytes(hex);
|
|
|
|
return bech32.encode(hrp, bech32.toWords(buf));
|
|
|
|
} catch (e) {
|
|
|
|
console.warn("Invalid hex", hex, e);
|
|
|
|
return "";
|
|
|
|
}
|
2023-01-06 14:36:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert hex pubkey to bech32 link url
|
|
|
|
* @param {string} hex
|
|
|
|
* @returns
|
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function profileLink(hex: HexKey) {
|
2023-01-09 16:18:34 +00:00
|
|
|
return `/p/${hexToBech32("npub", hex)}`;
|
2023-01-08 00:29:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reaction types
|
|
|
|
*/
|
|
|
|
export const Reaction = {
|
|
|
|
Positive: "+",
|
|
|
|
Negative: "-"
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return normalized reaction content
|
|
|
|
* @param {string} content
|
|
|
|
* @returns
|
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function normalizeReaction(content: string) {
|
2023-01-09 11:00:23 +00:00
|
|
|
switch (content) {
|
2023-01-08 00:29:59 +00:00
|
|
|
case "": return Reaction.Positive;
|
|
|
|
case "🤙": return Reaction.Positive;
|
|
|
|
case "❤️": return Reaction.Positive;
|
|
|
|
case "👍": return Reaction.Positive;
|
|
|
|
case "💯": return Reaction.Positive;
|
|
|
|
case "+": return Reaction.Positive;
|
|
|
|
case "-": return Reaction.Negative;
|
|
|
|
case "👎": return Reaction.Negative;
|
|
|
|
}
|
|
|
|
return content;
|
2023-01-09 11:00:23 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:03:15 +00:00
|
|
|
/**
|
|
|
|
* Get reactions to a specific event (#e + kind filter)
|
|
|
|
*/
|
|
|
|
export function getReactions(notes: TaggedRawEvent[], id: u256, kind = EventKind.Reaction) {
|
|
|
|
return notes?.filter(a => a.kind === kind && a.tags.some(a => a[0] === "e" && a[1] === id)) || [];
|
|
|
|
}
|
|
|
|
|
2023-01-09 11:00:23 +00:00
|
|
|
/**
|
|
|
|
* Converts LNURL service to LN Address
|
2023-01-15 19:40:47 +00:00
|
|
|
* @param lnurl
|
2023-01-09 11:00:23 +00:00
|
|
|
* @returns
|
|
|
|
*/
|
2023-01-15 19:40:47 +00:00
|
|
|
export function extractLnAddress(lnurl: string) {
|
2023-01-09 11:00:23 +00:00
|
|
|
// some clients incorrectly set this to LNURL service, patch this
|
|
|
|
if (lnurl.toLowerCase().startsWith("lnurl")) {
|
|
|
|
let url = bech32ToText(lnurl);
|
|
|
|
if (url.startsWith("http")) {
|
|
|
|
let parsedUri = new URL(url);
|
|
|
|
// is lightning address
|
|
|
|
if (parsedUri.pathname.startsWith("/.well-known/lnurlp/")) {
|
|
|
|
let pathParts = parsedUri.pathname.split('/');
|
|
|
|
let username = pathParts[pathParts.length - 1];
|
|
|
|
return `${username}@${parsedUri.hostname}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return lnurl;
|
2023-01-14 11:57:36 +00:00
|
|
|
}
|
2023-01-17 21:55:53 +00:00
|
|
|
|
|
|
|
export function unixNow() {
|
|
|
|
return Math.floor(new Date().getTime() / 1000);
|
|
|
|
}
|