mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Render client tags
This commit is contained in:
parent
caf59f4078
commit
23eb531305
4 changed files with 59 additions and 4 deletions
|
|
@ -56,4 +56,5 @@ export interface DittoEvent extends NostrEvent {
|
||||||
zap_message?: string;
|
zap_message?: string;
|
||||||
/** Language of the event (kind 1s are more accurate). */
|
/** Language of the event (kind 1s are more accurate). */
|
||||||
language?: LanguageCode;
|
language?: LanguageCode;
|
||||||
|
client?: DittoEvent;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { DittoDB, DittoTables } from '@ditto/db';
|
import { DittoDB, DittoTables } from '@ditto/db';
|
||||||
import { DittoConf } from '@ditto/conf';
|
import { DittoConf } from '@ditto/conf';
|
||||||
import { NStore } from '@nostrify/nostrify';
|
import { type NostrFilter, NStore } from '@nostrify/nostrify';
|
||||||
import { Kysely } from 'kysely';
|
import { Kysely } from 'kysely';
|
||||||
import { matchFilter } from 'nostr-tools';
|
import { matchFilter } from 'nostr-tools';
|
||||||
import { NSchema as n } from '@nostrify/nostrify';
|
import { NSchema as n } from '@nostrify/nostrify';
|
||||||
|
|
@ -50,6 +50,10 @@ async function hydrateEvents(opts: HydrateOpts): Promise<DittoEvent[]> {
|
||||||
cache.push(event);
|
cache.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const event of await gatherClients({ ...opts, events: cache })) {
|
||||||
|
cache.push(event);
|
||||||
|
}
|
||||||
|
|
||||||
const authorStats = await gatherAuthorStats(cache, db.kysely);
|
const authorStats = await gatherAuthorStats(cache, db.kysely);
|
||||||
const eventStats = await gatherEventStats(cache, db.kysely);
|
const eventStats = await gatherEventStats(cache, db.kysely);
|
||||||
|
|
||||||
|
|
@ -128,6 +132,16 @@ export function assembleEvents(
|
||||||
event.user = b.find((e) => matchFilter({ kinds: [30382], authors: [admin], '#d': [event.pubkey] }, e));
|
event.user = b.find((e) => matchFilter({ kinds: [30382], authors: [admin], '#d': [event.pubkey] }, e));
|
||||||
event.info = b.find((e) => matchFilter({ kinds: [30383], authors: [admin], '#d': [event.id] }, e));
|
event.info = b.find((e) => matchFilter({ kinds: [30383], authors: [admin], '#d': [event.id] }, e));
|
||||||
|
|
||||||
|
for (const [name, _value, addr] of event.tags) {
|
||||||
|
if (name === 'client' && addr) {
|
||||||
|
const match = addr.match(/^31990:([0-9a-f]{64}):(.+)$/);
|
||||||
|
if (match) {
|
||||||
|
const [, pubkey, d] = match;
|
||||||
|
event.client = b.find((e) => matchFilter({ kinds: [31990], authors: [pubkey], '#d': [d] }, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event.kind === 1) {
|
if (event.kind === 1) {
|
||||||
const id = findQuoteTag(event.tags)?.[1] || findQuoteInContent(event.content);
|
const id = findQuoteTag(event.tags)?.[1] || findQuoteInContent(event.content);
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|
@ -353,6 +367,28 @@ async function gatherInfo({ conf, events, relay, signal }: HydrateOpts): Promise
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gatherClients({ events, relay, signal }: HydrateOpts): Promise<DittoEvent[]> {
|
||||||
|
const filters: NostrFilter[] = [];
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
for (const [name, _value, addr] of event.tags) {
|
||||||
|
if (name === 'client' && addr) {
|
||||||
|
const match = addr.match(/^31990:([0-9a-f]{64}):(.+)$/);
|
||||||
|
if (match) {
|
||||||
|
const [, pubkey, d] = match;
|
||||||
|
filters.push({ kinds: [31990], authors: [pubkey], '#d': [d], limit: 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filters.length) {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return relay.query(filters, { signal });
|
||||||
|
}
|
||||||
|
|
||||||
/** Collect author stats from the events. */
|
/** Collect author stats from the events. */
|
||||||
async function gatherAuthorStats(
|
async function gatherAuthorStats(
|
||||||
events: DittoEvent[],
|
events: DittoEvent[],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { NostrEvent, NStore } from '@nostrify/nostrify';
|
import { NostrEvent, NSchema as n, NStore } from '@nostrify/nostrify';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
|
@ -118,11 +118,27 @@ async function renderStatus(
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as { name: string; count: number; me: boolean; url?: string }[]);
|
}, [] as { name: string; count: number; me: boolean; url?: string }[]);
|
||||||
|
|
||||||
|
let application: MastodonStatus['application'] = undefined;
|
||||||
|
|
||||||
|
if (event.client) {
|
||||||
|
const result = n.json().pipe(n.metadata()).safeParse(event.client.content);
|
||||||
|
if (result.success) {
|
||||||
|
const name = result.data.name ?? result.data.display_name ?? event.tags.find(([name]) => name === 'client')?.[1];
|
||||||
|
if (name) {
|
||||||
|
application = {
|
||||||
|
name,
|
||||||
|
website: result.data.website ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const expiresAt = new Date(Number(event.tags.find(([name]) => name === 'expiration')?.[1]) * 1000);
|
const expiresAt = new Date(Number(event.tags.find(([name]) => name === 'expiration')?.[1]) * 1000);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
account,
|
account,
|
||||||
|
application,
|
||||||
card: event.event_stats?.link_preview ?? null,
|
card: event.event_stats?.link_preview ?? null,
|
||||||
content: compatMentions + html,
|
content: compatMentions + html,
|
||||||
created_at: nostrDate(event.created_at).toISOString(),
|
created_at: nostrDate(event.created_at).toISOString(),
|
||||||
|
|
@ -142,7 +158,6 @@ async function renderStatus(
|
||||||
bookmarked: Boolean(bookmarkEvent),
|
bookmarked: Boolean(bookmarkEvent),
|
||||||
pinned: Boolean(pinEvent),
|
pinned: Boolean(pinEvent),
|
||||||
reblog: null,
|
reblog: null,
|
||||||
application: null,
|
|
||||||
media_attachments: media
|
media_attachments: media
|
||||||
.map((m) => renderAttachment({ tags: m }))
|
.map((m) => renderAttachment({ tags: m }))
|
||||||
.filter((m): m is MastodonAttachment => Boolean(m)),
|
.filter((m): m is MastodonAttachment => Boolean(m)),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ import type { MastodonPreviewCard } from './MastodonPreviewCard.ts';
|
||||||
export interface MastodonStatus {
|
export interface MastodonStatus {
|
||||||
id: string;
|
id: string;
|
||||||
account: MastodonAccount;
|
account: MastodonAccount;
|
||||||
|
application?: {
|
||||||
|
name: string;
|
||||||
|
website: string | null;
|
||||||
|
};
|
||||||
card: MastodonPreviewCard | null;
|
card: MastodonPreviewCard | null;
|
||||||
content: string;
|
content: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
|
|
@ -24,7 +28,6 @@ export interface MastodonStatus {
|
||||||
bookmarked: boolean;
|
bookmarked: boolean;
|
||||||
pinned: boolean;
|
pinned: boolean;
|
||||||
reblog: MastodonStatus | null;
|
reblog: MastodonStatus | null;
|
||||||
application: unknown;
|
|
||||||
media_attachments: MastodonAttachment[];
|
media_attachments: MastodonAttachment[];
|
||||||
mentions: unknown[];
|
mentions: unknown[];
|
||||||
tags: unknown[];
|
tags: unknown[];
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue