2023-06-30 14:05:57 +00:00
|
|
|
import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
2023-07-11 09:49:58 +00:00
|
|
|
import { Database_Contextual_View } from "../database.ts";
|
2023-06-30 14:05:57 +00:00
|
|
|
import {
|
|
|
|
ConnectionPool,
|
2023-08-03 09:13:16 +00:00
|
|
|
SubscriptionAlreadyExist,
|
2023-06-30 14:05:57 +00:00
|
|
|
} from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/relay.ts";
|
2023-07-16 15:04:23 +00:00
|
|
|
import { PublicKey } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts";
|
2023-06-30 14:05:57 +00:00
|
|
|
import {
|
|
|
|
groupBy,
|
|
|
|
NostrAccountContext,
|
|
|
|
NostrEvent,
|
|
|
|
NostrKind,
|
|
|
|
prepareNormalNostrEvent,
|
|
|
|
} from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts";
|
2023-07-16 15:04:23 +00:00
|
|
|
import { Parsed_Event, Profile_Nostr_Event } from "../nostr.ts";
|
2023-06-30 14:05:57 +00:00
|
|
|
|
|
|
|
// nip01 meta data
|
|
|
|
// https://github.com/nostr-protocol/nips/blob/master/05.md
|
|
|
|
// get the profile from a public key
|
|
|
|
export class UserMetaDataNotExist {
|
|
|
|
constructor(
|
|
|
|
public readonly publicKey: string,
|
|
|
|
public readonly relay: string,
|
|
|
|
) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function profilesStream(
|
|
|
|
publicKeys: Iterable<string>,
|
|
|
|
pool: ConnectionPool,
|
|
|
|
) {
|
|
|
|
const chan = csp.chan<[NostrEvent, string]>();
|
2023-08-26 19:59:03 +00:00
|
|
|
const publics = Array.from(publicKeys);
|
2023-06-30 14:05:57 +00:00
|
|
|
(async () => {
|
|
|
|
let resp = await pool.newSub(
|
2023-08-03 09:13:16 +00:00
|
|
|
"profilesStream",
|
2023-06-30 14:05:57 +00:00
|
|
|
{
|
2023-08-26 19:59:03 +00:00
|
|
|
authors: publics,
|
2023-06-30 14:05:57 +00:00
|
|
|
kinds: [NostrKind.META_DATA],
|
|
|
|
},
|
|
|
|
);
|
2023-08-03 09:13:16 +00:00
|
|
|
if (resp instanceof SubscriptionAlreadyExist) {
|
|
|
|
resp = await pool.updateSub(
|
|
|
|
"profilesStream",
|
|
|
|
{
|
2023-08-26 19:59:03 +00:00
|
|
|
authors: publics,
|
2023-08-03 09:13:16 +00:00
|
|
|
kinds: [NostrKind.META_DATA],
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
if (resp instanceof Error) {
|
2023-08-03 09:13:16 +00:00
|
|
|
console.error(resp.message);
|
|
|
|
return;
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
2023-08-26 19:59:03 +00:00
|
|
|
console.log("new profile stream", publics);
|
2023-06-30 14:05:57 +00:00
|
|
|
for await (let { res: nostrMessage, url: relayUrl } of resp) {
|
|
|
|
if (nostrMessage.type === "EVENT" && nostrMessage.event.content) {
|
|
|
|
await chan.put([
|
|
|
|
nostrMessage.event,
|
|
|
|
relayUrl,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await chan.close(`pool sub has been clsoed`);
|
|
|
|
})();
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function saveProfile(
|
|
|
|
profile: ProfileData,
|
|
|
|
sender: NostrAccountContext,
|
|
|
|
pool: ConnectionPool,
|
|
|
|
) {
|
|
|
|
const event = await prepareNormalNostrEvent(
|
|
|
|
sender,
|
|
|
|
NostrKind.META_DATA,
|
|
|
|
[],
|
|
|
|
JSON.stringify(profile),
|
|
|
|
);
|
|
|
|
pool.sendEvent(event);
|
|
|
|
}
|
|
|
|
|
2023-07-14 10:59:25 +00:00
|
|
|
export function getProfileEvent(
|
|
|
|
db: Database_Contextual_View,
|
|
|
|
pubkey: PublicKey,
|
|
|
|
): Profile_Nostr_Event | undefined {
|
2023-07-14 14:13:15 +00:00
|
|
|
const events: Profile_Nostr_Event[] = [];
|
2023-07-14 10:59:25 +00:00
|
|
|
for (const e of db.events) {
|
|
|
|
if (e.kind === NostrKind.META_DATA && e.pubkey === pubkey.hex) {
|
2023-07-14 14:13:15 +00:00
|
|
|
events.push(e);
|
2023-07-14 10:59:25 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
if (events.length == 0) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
events.sort((e1, e2) => e2.created_at - e1.created_at);
|
|
|
|
const newest = events[0];
|
2023-07-14 14:13:15 +00:00
|
|
|
return newest;
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 10:59:25 +00:00
|
|
|
export function getProfilesByName(db: Database_Contextual_View, name: string): Profile_Nostr_Event[] {
|
2023-07-14 14:13:15 +00:00
|
|
|
const events: Profile_Nostr_Event[] = [];
|
2023-07-14 10:59:25 +00:00
|
|
|
for (const e of db.events) {
|
|
|
|
if (e.kind === NostrKind.META_DATA) {
|
2023-07-14 14:13:15 +00:00
|
|
|
events.push(e);
|
2023-07-14 10:59:25 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
if (events.length == 0) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
const profilesPerUser = groupBy(events, (e) => e.pubkey);
|
|
|
|
|
|
|
|
const result = [];
|
|
|
|
for (const events of profilesPerUser.values()) {
|
|
|
|
events.sort((e1, e2) => e2.created_at - e1.created_at);
|
2023-07-14 14:13:15 +00:00
|
|
|
const p = events[0];
|
2023-07-14 10:59:25 +00:00
|
|
|
if (p.profile.name && p.profile.name?.toLocaleLowerCase().indexOf(name.toLowerCase()) != -1) {
|
2023-06-30 14:05:57 +00:00
|
|
|
result.push(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getProfiles(
|
2023-07-11 09:49:58 +00:00
|
|
|
db: Database_Contextual_View,
|
2023-06-30 14:05:57 +00:00
|
|
|
pubkeys: Set<string>,
|
2023-07-14 10:59:25 +00:00
|
|
|
): Map<string, /*pubkey*/ Profile_Nostr_Event | undefined> {
|
|
|
|
const contacts: Map<string, Profile_Nostr_Event | undefined> = new Map();
|
2023-06-30 14:05:57 +00:00
|
|
|
for (const key of pubkeys) {
|
2023-07-04 06:37:14 +00:00
|
|
|
const event = getProfileEvent(db, PublicKey.FromHex(key) as PublicKey);
|
2023-06-30 14:05:57 +00:00
|
|
|
contacts.set(key, event);
|
|
|
|
}
|
|
|
|
return contacts;
|
|
|
|
}
|
|
|
|
|
|
|
|
// aka user profile
|
|
|
|
export interface ProfileData {
|
|
|
|
name?: string;
|
|
|
|
picture?: string;
|
|
|
|
about?: string;
|
|
|
|
website?: string;
|
|
|
|
banner?: string;
|
|
|
|
[key: string]: string | undefined;
|
|
|
|
}
|
|
|
|
|
2023-07-14 10:59:25 +00:00
|
|
|
export function ProfileFromNostrEvent(
|
2023-07-15 05:58:03 +00:00
|
|
|
event: Parsed_Event<NostrKind.META_DATA>,
|
2023-07-14 14:13:15 +00:00
|
|
|
) {
|
|
|
|
const profileData = parseProfileData(event.content);
|
|
|
|
if (profileData instanceof Error) {
|
|
|
|
return profileData;
|
2023-07-07 08:31:06 +00:00
|
|
|
}
|
2023-07-14 14:13:15 +00:00
|
|
|
const e: Profile_Nostr_Event = {
|
2023-06-30 14:05:57 +00:00
|
|
|
kind: event.kind,
|
|
|
|
id: event.id,
|
|
|
|
sig: event.sig,
|
|
|
|
created_at: event.created_at,
|
|
|
|
pubkey: event.pubkey,
|
|
|
|
tags: event.tags,
|
2023-07-14 10:59:25 +00:00
|
|
|
content: event.content,
|
|
|
|
parsedTags: event.parsedTags,
|
|
|
|
profile: profileData,
|
2023-07-15 05:58:03 +00:00
|
|
|
publicKey: event.publicKey,
|
2023-06-30 14:05:57 +00:00
|
|
|
};
|
2023-07-14 14:13:15 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function parseProfileData(content: string) {
|
|
|
|
try {
|
|
|
|
return JSON.parse(content) as ProfileData;
|
|
|
|
} catch (e) {
|
|
|
|
return e as Error;
|
|
|
|
}
|
2023-06-30 14:05:57 +00:00
|
|
|
}
|