snort/src/Util.ts

213 lines
4.8 KiB
TypeScript
Raw Normal View History

2023-01-06 14:36:13 +00:00
import * as secp from "@noble/secp256k1";
import { sha256 as hash } from "@noble/hashes/sha256";
2023-01-06 14:36:13 +00:00
import { bech32 } from "bech32";
2023-02-07 19:47:57 +00:00
import { HexKey, TaggedRawEvent, u256 } from "Nostr";
2023-01-20 11:11:50 +00:00
import EventKind from "Nostr/EventKind";
2023-02-08 05:56:00 +00:00
import { MessageDescriptor } from "react-intl";
2023-01-03 16:55:51 +00:00
2023-02-03 21:38:14 +00:00
export const sha256 = (str: string) => {
return secp.utils.bytesToHex(hash(str));
};
2023-02-03 21:38:14 +00:00
2023-01-16 17:48:25 +00:00
export async function openFile(): Promise<File | undefined> {
2023-02-07 19:47:57 +00:00
return new Promise((resolve) => {
const elm = document.createElement("input");
elm.type = "file";
elm.onchange = (e: Event) => {
2023-02-07 19:47:57 +00:00
const elm = e.target as HTMLInputElement;
if (elm.files) {
resolve(elm.files[0]);
} else {
resolve(undefined);
}
};
elm.click();
});
2023-01-03 16:55:51 +00:00
}
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) {
const hrp = ["note", "npub", "nsec"];
try {
if (hrp.some((a) => id.startsWith(a))) {
return bech32ToHex(id);
}
2023-02-07 19:47:57 +00:00
} catch (e) {
// Ignore the error.
}
return id;
2023-01-06 14:36:13 +00:00
}
2023-01-15 19:40:47 +00:00
export function bech32ToHex(str: string) {
2023-02-07 19:47:57 +00:00
const nKey = bech32.decode(str);
const buff = bech32.fromWords(nKey.words);
return secp.utils.bytesToHex(Uint8Array.from(buff));
2023-01-06 14:36:13 +00:00
}
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
* @returns
2023-01-07 20:54:12 +00:00
*/
2023-01-15 19:40:47 +00:00
export function bech32ToText(str: string) {
2023-02-07 19:47:57 +00:00
const decoded = bech32.decode(str, 1000);
const buf = bech32.fromWords(decoded.words);
return new TextDecoder().decode(Uint8Array.from(buf));
2023-01-07 20:54:12 +00:00
}
2023-01-06 14:36:13 +00:00
/**
* Convert hex note id to bech32 link url
* @param hex
* @returns
2023-01-06 14:36:13 +00:00
*/
2023-01-15 19:40:47 +00:00
export function eventLink(hex: u256) {
return `/e/${hexToBech32("note", hex)}`;
2023-01-09 16:18:34 +00:00
}
/**
* Convert hex to bech32
* @param {string} hex
*/
2023-01-15 19:40:47 +00:00
export function hexToBech32(hrp: string, hex: string) {
if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) {
return "";
}
2023-01-15 19:40:47 +00:00
try {
2023-02-07 19:47:57 +00:00
const 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-06 14:36:13 +00:00
*/
2023-01-15 19:40:47 +00:00
export function profileLink(hex: HexKey) {
return `/p/${hexToBech32("npub", hex)}`;
2023-01-08 00:29:59 +00:00
}
/**
* Reaction types
*/
export const Reaction = {
Positive: "+",
Negative: "-",
2023-01-08 00:29:59 +00:00
};
/**
* Return normalized reaction content
* @param {string} content
* @returns
2023-01-08 00:29:59 +00:00
*/
2023-01-15 19:40:47 +00:00
export function normalizeReaction(content: string) {
switch (content) {
case "-":
return Reaction.Negative;
case "👎":
return Reaction.Negative;
2023-02-09 11:32:35 +00:00
default:
return Reaction.Positive;
}
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-17 13:03:15 +00:00
}
2023-01-09 11:00:23 +00:00
/**
* Converts LNURL service to LN Address
* @param lnurl
* @returns
2023-01-09 11:00:23 +00:00
*/
2023-01-15 19:40:47 +00:00
export function extractLnAddress(lnurl: string) {
// some clients incorrectly set this to LNURL service, patch this
if (lnurl.toLowerCase().startsWith("lnurl")) {
2023-02-07 19:47:57 +00:00
const url = bech32ToText(lnurl);
if (url.startsWith("http")) {
2023-02-07 19:47:57 +00:00
const parsedUri = new URL(url);
// is lightning address
if (parsedUri.pathname.startsWith("/.well-known/lnurlp/")) {
2023-02-07 19:47:57 +00:00
const pathParts = parsedUri.pathname.split("/");
const username = pathParts[pathParts.length - 1];
return `${username}@${parsedUri.hostname}`;
}
2023-01-09 11:00:23 +00:00
}
}
return lnurl;
}
2023-01-17 21:55:53 +00:00
export function unixNow() {
return Math.floor(new Date().getTime() / 1000);
}
2023-01-24 14:09:56 +00:00
/**
* Simple debounce
* @param timeout Time until falling edge
* @param fn Callack to run on falling edge
* @returns Cancel timeout function
*/
export function debounce(timeout: number, fn: () => void) {
2023-02-07 19:47:57 +00:00
const t = setTimeout(fn, timeout);
return () => clearTimeout(t);
2023-02-03 21:38:14 +00:00
}
2023-02-08 05:56:00 +00:00
export function addIdAndDefaultMessageToMessages(
messages: Record<string, string>,
messageIdPrefix: string
) {
const result: Record<string, MessageDescriptor> = {};
for (const key in messages) {
result[key] = {
id: `${messageIdPrefix}.${key}`,
defaultMessage: messages[key],
};
}
return result;
}
2023-02-08 21:10:26 +00:00
export function dedupeByPubkey(events: TaggedRawEvent[]) {
const deduped = events.reduce(
({ list, seen }: { list: TaggedRawEvent[]; seen: Set<HexKey> }, ev) => {
if (seen.has(ev.pubkey)) {
return { list, seen };
}
seen.add(ev.pubkey);
return {
seen,
list: [...list, ev],
};
},
{ list: [], seen: new Set([]) }
);
return deduped.list as TaggedRawEvent[];
}
2023-02-07 19:47:57 +00:00
export function unwrap<T>(v: T | undefined | null): T {
if (v === undefined || v === null) {
throw new Error("missing value");
}
return v;
}