blowater/app/nostr.ts

216 lines
6.0 KiB
TypeScript
Raw Normal View History

2023-06-30 14:05:57 +00:00
/*
Extension to common Nostr types
*/
import {
NostrKind,
prepareEncryptedNostrEvent,
prepareNormalNostrEvent,
PublicKey,
TagPubKey,
} from "@blowater/nostr-sdk";
2023-07-14 10:59:25 +00:00
import { ProfileData } from "./features/profile.ts";
import * as nostr from "@blowater/nostr-sdk";
2023-06-30 14:05:57 +00:00
type TotolChunks = string;
type ChunkIndex = string; // 0-indexed
type GroupLeadEventID = string;
export type TagImage = ["image", GroupLeadEventID, TotolChunks, ChunkIndex];
export type TagClient = ["client", "blowater"];
export type TagLamportTimestamp = ["lamport", string];
export type TagReply = ["e", nostr.EventID, RelayURL, Marker];
type Marker = "reply" | "root" | "mention";
type RelayURL = string;
export type Tag = nostr.Tag | TagImage | TagClient | TagLamportTimestamp | TagReply;
export type Tags = {
2023-06-30 14:05:57 +00:00
image?: [GroupLeadEventID, TotolChunks, ChunkIndex];
lamport_timestamp?: number;
reply?: [nostr.EventID, RelayURL, "reply"];
root?: [nostr.EventID, RelayURL, "root"];
} & nostr.Tags;
2023-07-14 10:38:45 +00:00
type Event = nostr.NostrEvent<NostrKind, Tag>;
2023-09-12 14:51:27 +00:00
export type parsedTagsEvent<Kind extends NostrKind = NostrKind> = nostr.NostrEvent<Kind> & {
readonly parsedTags: Tags;
};
export type Parsed_Event<Kind extends NostrKind = NostrKind> = parsedTagsEvent<Kind> & {
readonly publicKey: PublicKey;
};
// content is either JSON, encrypted or other format that's should not be rendered directly
2023-09-19 19:38:38 +00:00
export type Encrypted_Kind = NostrKind.DIRECT_MESSAGE;
2023-09-12 14:51:27 +00:00
export type Non_Plain_Text_Kind = Encrypted_Kind | NostrKind.META_DATA;
export type Profile_Nostr_Event = Parsed_Event<NostrKind.META_DATA> & {
profile: ProfileData;
};
export type DirectedMessage_Event = Parsed_Event<NostrKind.DIRECT_MESSAGE> & {
decryptedContent: string;
};
2023-09-19 19:38:38 +00:00
export type Encrypted_Event = DirectedMessage_Event;
2023-09-12 14:51:27 +00:00
export type CustomAppData = PinConversation | UnpinConversation | UserLogin;
2023-09-12 14:51:27 +00:00
export type PinConversation = {
type: "PinConversation";
2023-09-12 14:51:27 +00:00
pubkey: string;
};
2023-11-25 14:29:50 +00:00
export type PinConversationRelay = {
type: "PinConversation";
pubkey: string;
lamport: number;
};
export type UnpinConversation = {
type: "UnpinConversation";
2023-09-12 14:51:27 +00:00
pubkey: string;
};
2023-11-25 14:29:50 +00:00
export type UnpinConversationRelay = {
type: "UnpinConversation";
pubkey: string;
lamport: number;
};
2023-09-12 14:51:27 +00:00
export type UserLogin = {
type: "UserLogin";
};
2023-06-30 14:05:57 +00:00
export function getTags(event: Event): Tags {
const tags: Tags = {
p: [],
e: [],
};
for (const tag of event.tags) {
switch (tag[0]) {
case "p":
tags.p.push(tag[1]);
break;
case "e":
if (tag[3] == "reply") {
const [_1, EventID, RelayURL, _2] = tag;
tags.reply = [EventID, RelayURL as string, "reply"];
} else if (tag[3] == "root") {
const [_1, EventID, RelayURL, _2] = tag;
tags.root = [EventID, RelayURL as string, "root"];
2023-07-07 09:28:46 +00:00
} else if (tag[1] != "") {
2023-06-30 14:05:57 +00:00
tags.e.push(tag[1]);
}
break;
case "image":
const [_, GroupLeadEventID, TotolChunks, ChunkIndex] = tag;
tags.image = [GroupLeadEventID, TotolChunks, ChunkIndex];
break;
case "client":
tags.client = tag[1];
break;
case "lamport":
tags.lamport_timestamp = Number(tag[1]);
break;
}
}
return tags;
}
2023-09-11 20:40:56 +00:00
export async function prepareNostrImageEvent(
2023-06-30 14:05:57 +00:00
sender: nostr.NostrAccountContext,
2023-07-11 09:19:57 +00:00
receiverPublicKey: PublicKey,
2023-06-30 14:05:57 +00:00
blob: Blob,
kind: nostr.NostrKind,
2023-10-21 11:29:47 +00:00
): Promise<nostr.NostrEvent | Error> {
2023-06-30 14:05:57 +00:00
const binaryContent = await nostr.blobToBase64(blob);
2024-01-17 08:37:29 +00:00
const encrypted = await sender.encrypt(receiverPublicKey.hex, binaryContent, "nip44");
2023-09-11 20:40:56 +00:00
if (encrypted instanceof Error) {
return encrypted;
2023-06-30 14:05:57 +00:00
}
2023-09-11 20:40:56 +00:00
2023-09-12 14:51:27 +00:00
const event: nostr.UnsignedNostrEvent = {
2023-09-11 20:40:56 +00:00
created_at: Math.floor(Date.now() / 1000),
kind: kind,
pubkey: sender.publicKey.hex,
tags: [
["p", receiverPublicKey.hex],
2023-10-21 11:29:47 +00:00
["image"],
2023-09-11 20:40:56 +00:00
],
content: encrypted,
};
const signedEvent = await sender.signEvent(event);
2023-10-21 11:29:47 +00:00
return signedEvent;
2023-06-30 14:05:57 +00:00
}
2024-04-12 09:04:58 +00:00
export async function prepareReplyEncryptEvent(
author: nostr.NostrAccountContext,
args: {
encryptKey: PublicKey;
kind: NostrKind;
tags: Tag[];
algorithm?: "nip4" | "nip44";
targetEvent: nostr.NostrEvent;
content: string;
},
): Promise<nostr.NostrEvent | Error> {
const { targetEvent, content, tags, encryptKey, kind, algorithm } = args;
return prepareEncryptedNostrEvent(author, {
encryptKey,
kind,
content,
tags: [
[
"e",
targetEvent.id,
"",
"reply",
],
...tags,
],
algorithm,
});
}
2023-09-08 13:09:14 +00:00
export async function prepareReplyEvent(
2023-06-30 14:05:57 +00:00
sender: nostr.NostrAccountContext,
2024-03-27 07:38:12 +00:00
args: {
targetEvent: nostr.NostrEvent;
tags: Tag[];
content: string;
currentRelay: string;
},
2023-06-30 14:05:57 +00:00
): Promise<nostr.NostrEvent | Error> {
2024-03-27 07:38:12 +00:00
const ps = getTags(args.targetEvent).p;
2023-06-30 14:05:57 +00:00
return prepareNormalNostrEvent(
sender,
2023-10-26 08:42:04 +00:00
{
2024-03-27 07:38:12 +00:00
kind: args.targetEvent.kind,
2023-10-26 08:42:04 +00:00
tags: [
2023-06-30 14:05:57 +00:00
[
2023-10-26 08:42:04 +00:00
"e",
2024-03-27 07:38:12 +00:00
args.targetEvent.id,
args.currentRelay,
2023-10-26 08:42:04 +00:00
"reply",
],
...ps.map((p) =>
[
"p",
p,
] as TagPubKey
),
2024-03-27 07:38:12 +00:00
...args.tags,
2023-10-26 08:42:04 +00:00
],
2024-03-27 07:38:12 +00:00
content: args.content,
2023-10-26 08:42:04 +00:00
},
2023-06-30 14:05:57 +00:00
);
}
2023-07-15 05:58:03 +00:00
export function compare(a: parsedTagsEvent, b: parsedTagsEvent) {
2023-07-13 09:06:35 +00:00
if (a.parsedTags.lamport_timestamp && b.parsedTags.lamport_timestamp) {
return a.parsedTags.lamport_timestamp - b.parsedTags.lamport_timestamp;
2023-06-30 14:05:57 +00:00
}
return a.created_at - b.created_at;
}