From 178a3c4d0e488dba74ab21c2b5686f598dedcc4d Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 5 Apr 2024 19:44:12 -0300 Subject: [PATCH] feat: repost functionality --- src/app.ts | 2 ++ src/controllers/api/statuses.ts | 28 +++++++++++++++++++++++++++- src/controllers/api/timelines.ts | 13 +++++++++---- src/views/mastodon/statuses.ts | 14 +++++++++++++- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/app.ts b/src/app.ts index 3866760e..743620ad 100644 --- a/src/app.ts +++ b/src/app.ts @@ -62,6 +62,7 @@ import { favouritedByController, pinController, rebloggedByController, + reblogStatusController, statusController, unbookmarkController, unpinController, @@ -167,6 +168,7 @@ app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/unbookmark', requirePubkey, unbookm app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/pin', requirePubkey, pinController); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/unpin', requirePubkey, unpinController); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/zap', requirePubkey, zapController); +app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/reblog', requirePubkey, reblogStatusController); app.post('/api/v1/statuses', requirePubkey, createStatusController); app.delete('/api/v1/statuses/:id{[0-9a-f]{64}}', requirePubkey, deleteStatusController); diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index 0b4216e2..7abd3e5b 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -7,7 +7,7 @@ import { jsonMetaContentSchema } from '@/schemas/nostr.ts'; import { addTag, deleteTag } from '@/tags.ts'; import { createEvent, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts'; import { renderEventAccounts } from '@/views.ts'; -import { renderStatus } from '@/views/mastodon/statuses.ts'; +import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts'; import { getLnurl } from '@/utils/lnurl.ts'; const createStatusSchema = z.object({ @@ -173,6 +173,31 @@ const favouritedByController: AppController = (c) => { return renderEventAccounts(c, [{ kinds: [7], '#e': [id], ...params }]); }; +/** https://docs.joinmastodon.org/methods/statuses/#boost */ +const reblogStatusController: AppController = async (c) => { + const eventId = c.req.param('id'); + + const event = await getEvent(eventId, { + kind: 1, + }); + + if (event == undefined) { + return c.json({ error: 'Event not found.' }, 404); + } + + const tags: string[][] = [['e', event.id], ['p', event.pubkey]]; + + const reblogEvent = await createEvent({ + kind: 6, + content: JSON.stringify(event), + tags, + }, c); + + const status = await renderReblog(reblogEvent, reblogEvent.pubkey); + + return c.json(status); +}; + const rebloggedByController: AppController = (c) => { const id = c.req.param('id'); const params = paginationSchema.parse(c.req.query()); @@ -339,6 +364,7 @@ export { favouriteController, favouritedByController, pinController, + reblogStatusController, rebloggedByController, statusController, unbookmarkController, diff --git a/src/controllers/api/timelines.ts b/src/controllers/api/timelines.ts index e633ce3d..9dd07397 100644 --- a/src/controllers/api/timelines.ts +++ b/src/controllers/api/timelines.ts @@ -7,13 +7,13 @@ import { booleanParamSchema } from '@/schema.ts'; import { eventsDB } from '@/storages.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { paginated, paginationSchema } from '@/utils/api.ts'; -import { renderStatus } from '@/views/mastodon/statuses.ts'; +import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts'; const homeTimelineController: AppController = async (c) => { const params = paginationSchema.parse(c.req.query()); const pubkey = c.get('pubkey')!; const authors = await getFeedPubkeys(pubkey); - return renderStatuses(c, [{ authors, kinds: [1], ...params }]); + return renderStatuses(c, [{ authors, kinds: [1, 6], ...params }]); }; const publicQuerySchema = z.object({ @@ -25,7 +25,7 @@ const publicTimelineController: AppController = (c) => { const params = paginationSchema.parse(c.req.query()); const { local, instance } = publicQuerySchema.parse(c.req.query()); - const filter: NostrFilter = { kinds: [1], ...params }; + const filter: NostrFilter = { kinds: [1, 6], ...params }; if (local) { filter.search = `domain:${Conf.url.host}`; @@ -56,7 +56,12 @@ async function renderStatuses(c: AppContext, filters: NostrFilter[]) { return c.json([]); } - const statuses = await Promise.all(events.map((event) => renderStatus(event, c.get('pubkey')))); + const statuses = await Promise.all(events.map((event) => { + if (event.kind == 6) { + return renderReblog(event, c.get('pubkey')); + } + return renderStatus(event, c.get('pubkey')); + })); return paginated(c, events, statuses); } diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index 172a2b6b..408d360a 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -98,6 +98,18 @@ async function renderStatus(event: DittoEvent, viewerPubkey?: string) { }; } +async function renderReblog(event: DittoEvent, viewerPubkey?: string) { + if (event.author == undefined) return; + + const reblog = await renderStatus(JSON.parse(event.content), viewerPubkey); + return { + id: event.id, + account: await renderAccount(event.author), + reblogged: true, + reblog, + }; +} + async function toMention(pubkey: string) { const author = await getAuthor(pubkey); const account = author ? await renderAccount(author) : undefined; @@ -134,4 +146,4 @@ function buildInlineRecipients(mentions: Mention[]): string { return `${elements.join(' ')} `; } -export { renderStatus }; +export { renderStatus, renderReblog };