From 6748e13a36231a6a3d25ebf8a23fac8ce2aa3e5d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 18 Mar 2023 14:49:44 -0500 Subject: [PATCH] Make Home feed kind of work --- src/api/home.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/app.ts | 4 +++- src/client.ts | 37 +++++++++++++++++++++++++++++-------- src/event.ts | 22 ++++------------------ 4 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 src/api/home.ts diff --git a/src/api/home.ts b/src/api/home.ts new file mode 100644 index 00000000..81995bf5 --- /dev/null +++ b/src/api/home.ts @@ -0,0 +1,48 @@ +import { fetchFeed, fetchFollows } from '../client.ts'; +import { getKeys } from '../utils.ts'; + +import type { Context } from '@/deps.ts'; +import type { SignedEvent } from '../event.ts'; + +async function homeController(c: Context) { + const keys = getKeys(c); + if (!keys) { + return c.json({ error: 'Unauthorized' }, 401); + } + + const follows = await fetchFollows(keys.pubkey); + if (!follows) { + return c.json([]); + } + + const events = await fetchFeed(follows); + const statuses = events.map(toStatus); + + return c.json(statuses); +} + +interface Account { + id: string; + acct: string; + username: string; +} + +interface Status { + id: string; + content: string; + account: Account; +} + +function toStatus(event: SignedEvent<1>): Status { + return { + id: event.id, + content: event.content, + account: { + id: event.pubkey, + acct: event.pubkey, + username: event.pubkey, + }, + }; +} + +export default homeController; diff --git a/src/app.ts b/src/app.ts index 19dbb11c..58feba45 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,6 +3,7 @@ import { cors, Hono } from '@/deps.ts'; import { credentialsController } from './api/accounts.ts'; import { appCredentialsController, createAppController } from './api/apps.ts'; import { emptyArrayController, emptyObjectController } from './api/fallback.ts'; +import homeController from './api/home.ts'; import instanceController from './api/instance.ts'; import { createTokenController } from './api/oauth.ts'; import { createStatusController } from './api/statuses.ts'; @@ -23,8 +24,9 @@ app.get('/api/v1/accounts/verify_credentials', credentialsController); app.post('/api/v1/statuses', createStatusController); +app.get('/api/v1/timelines/home', homeController); + // Not (yet) implemented. -app.get('/api/v1/timelines/*', emptyArrayController); app.get('/api/v1/notifications', emptyArrayController); app.get('/api/v1/accounts/:id/statuses', emptyArrayController); app.get('/api/v1/bookmarks', emptyArrayController); diff --git a/src/client.ts b/src/client.ts index 19268e34..287fbcaf 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2,30 +2,30 @@ import { Author, RelayPool } from '@/deps.ts'; import { poolRelays } from './config.ts'; -import type { Event } from './event.ts'; +import type { Event, SignedEvent } from './event.ts'; const pool = new RelayPool(poolRelays); /** Fetch a Nostr event by its ID. */ -const fetchEvent = async (id: string): Promise => { - const event = await (pool.getEventById(id, poolRelays, 0) as Promise); +const fetchEvent = async (id: string): Promise => { + const event = await (pool.getEventById(id, poolRelays, 0) as Promise); return event?.id === id ? event : null; }; /** Fetch a Nostr `set_medatadata` event for a user's pubkey. */ -const fetchUser = async (pubkey: string): Promise | null> => { +const fetchUser = async (pubkey: string): Promise | null> => { const author = new Author(pool, poolRelays, pubkey); - const event: Event<0> | null = await new Promise((resolve) => author.metaData(resolve, 0)); + const event: SignedEvent<0> | null = await new Promise((resolve) => author.metaData(resolve, 0)); return event?.pubkey === pubkey ? event : null; }; /** Fetch users the given pubkey follows. */ -const fetchFollows = (pubkey: string): Promise | null> => { +const fetchFollows = (pubkey: string): Promise | null> => { return new Promise((resolve) => { pool.subscribe( [{ authors: [pubkey], kinds: [3] }], poolRelays, - (event: Event<3> | null) => { + (event: SignedEvent<3> | null) => { resolve(event?.pubkey === pubkey ? event : null); }, undefined, @@ -34,4 +34,25 @@ const fetchFollows = (pubkey: string): Promise | null> => { }); }; -export { fetchEvent, fetchFollows, fetchUser, pool }; +/** Fetch 20 events from people the user follows. */ +function fetchFeed(event3: Event<3>): Promise[]> { + const authors = event3.tags.filter((tag) => tag[0] === 'p').map((tag) => tag[1]); + const results: SignedEvent<1>[] = []; + + return new Promise((resolve) => { + pool.subscribe( + [{ authors, kinds: [1], limit: 20 }], + poolRelays, + (event: SignedEvent<1> | null) => { + if (event) { + results.push(event); + } + }, + void 0, + () => resolve(results), + { unsubscribeOnEose: true }, + ); + }); +} + +export { fetchEvent, fetchFeed, fetchFollows, fetchUser, pool }; diff --git a/src/event.ts b/src/event.ts index 14e47a7d..2b4ff62b 100644 --- a/src/event.ts +++ b/src/event.ts @@ -1,20 +1,4 @@ -enum Kind { - Metadata = 0, - Text = 1, - RecommendRelay = 2, - Contacts = 3, - EncryptedDirectMessage = 4, - EventDeletion = 5, - DeprecatedRepost = 6, - Reaction = 7, - ChannelCreation = 40, - ChannelMetadata = 41, - ChannelMessage = 42, - ChannelHideMessage = 43, - ChannelMuteUser = 44, -} - -interface Event { +interface Event { id?: string; sig?: string; kind: K; @@ -24,4 +8,6 @@ interface Event { created_at: number; } -export type { Event, Kind }; +type SignedEvent = Event & { id: string; sig: string }; + +export type { Event, SignedEvent };