mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Add interfaces for Mastodon entity types, hide deactivated accounts
This commit is contained in:
parent
06db5d2b1d
commit
e63ee9b5a3
7 changed files with 143 additions and 27 deletions
58
src/entities/MastodonAccount.ts
Normal file
58
src/entities/MastodonAccount.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/** Mastodon account entity, including supported extensions from Pleroma, etc. */
|
||||
export interface MastodonAccount {
|
||||
id: string;
|
||||
acct: string;
|
||||
avatar: string;
|
||||
avatar_static: string;
|
||||
bot: boolean;
|
||||
created_at: string;
|
||||
discoverable: boolean;
|
||||
display_name: string;
|
||||
emojis: {
|
||||
shortcode: string;
|
||||
static_url: string;
|
||||
url: string;
|
||||
}[];
|
||||
fields: unknown[];
|
||||
follow_requests_count: number;
|
||||
followers_count: number;
|
||||
following_count: number;
|
||||
fqn: string;
|
||||
header: string;
|
||||
header_static: string;
|
||||
last_status_at: string | null;
|
||||
locked: boolean;
|
||||
note: string;
|
||||
roles: unknown[];
|
||||
source?: {
|
||||
fields: unknown[];
|
||||
language: string;
|
||||
note: string;
|
||||
privacy: string;
|
||||
sensitive: boolean;
|
||||
follow_requests_count: number;
|
||||
nostr: {
|
||||
nip05?: string;
|
||||
};
|
||||
};
|
||||
statuses_count: number;
|
||||
url: string;
|
||||
username: string;
|
||||
ditto: {
|
||||
accepts_zaps: boolean;
|
||||
};
|
||||
pleroma: {
|
||||
deactivated: boolean;
|
||||
is_admin: boolean;
|
||||
is_moderator: boolean;
|
||||
is_suggested: boolean;
|
||||
is_local: boolean;
|
||||
settings_store: unknown;
|
||||
tags: string[];
|
||||
};
|
||||
nostr: {
|
||||
pubkey: string;
|
||||
lud16?: string;
|
||||
};
|
||||
website?: string;
|
||||
}
|
||||
6
src/entities/MastodonMention.ts
Normal file
6
src/entities/MastodonMention.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface MastodonMention {
|
||||
acct: string;
|
||||
id: string;
|
||||
url: string;
|
||||
username: string;
|
||||
}
|
||||
42
src/entities/MastodonStatus.ts
Normal file
42
src/entities/MastodonStatus.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { MastodonAccount } from '@/entities/MastodonAccount.ts';
|
||||
import { PreviewCard } from '@/entities/PreviewCard.ts';
|
||||
|
||||
export interface MastodonStatus {
|
||||
id: string;
|
||||
account: MastodonAccount;
|
||||
card: PreviewCard | null;
|
||||
content: string;
|
||||
created_at: string;
|
||||
in_reply_to_id: string | null;
|
||||
in_reply_to_account_id: string | null;
|
||||
sensitive: boolean;
|
||||
spoiler_text: string;
|
||||
visibility: string;
|
||||
language: string | null;
|
||||
replies_count: number;
|
||||
reblogs_count: number;
|
||||
favourites_count: number;
|
||||
zaps_amount: number;
|
||||
favourited: boolean;
|
||||
reblogged: boolean;
|
||||
muted: boolean;
|
||||
bookmarked: boolean;
|
||||
pinned: boolean;
|
||||
reblog: MastodonStatus | null;
|
||||
application: unknown;
|
||||
media_attachments: unknown[];
|
||||
mentions: unknown[];
|
||||
tags: unknown[];
|
||||
emojis: unknown[];
|
||||
poll: unknown;
|
||||
quote?: MastodonStatus | null;
|
||||
quote_id: string | null;
|
||||
uri: string;
|
||||
url: string;
|
||||
zapped: boolean;
|
||||
pleroma: {
|
||||
emoji_reactions: { name: string; count: number; me: boolean }[];
|
||||
expires_at?: string;
|
||||
quotes_count: number;
|
||||
};
|
||||
}
|
||||
16
src/entities/PreviewCard.ts
Normal file
16
src/entities/PreviewCard.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export interface PreviewCard {
|
||||
url: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: 'link' | 'photo' | 'video' | 'rich';
|
||||
author_name: string;
|
||||
author_url: string;
|
||||
provider_name: string;
|
||||
provider_url: string;
|
||||
html: string;
|
||||
width: number;
|
||||
height: number;
|
||||
image: string | null;
|
||||
embed_url: string;
|
||||
blurhash: string | null;
|
||||
}
|
||||
|
|
@ -3,28 +3,12 @@ import Debug from '@soapbox/stickynotes/debug';
|
|||
import DOMPurify from 'isomorphic-dompurify';
|
||||
import { unfurl } from 'unfurl.js';
|
||||
|
||||
import { PreviewCard } from '@/entities/PreviewCard.ts';
|
||||
import { Time } from '@/utils/time.ts';
|
||||
import { fetchWorker } from '@/workers/fetch.ts';
|
||||
|
||||
const debug = Debug('ditto:unfurl');
|
||||
|
||||
interface PreviewCard {
|
||||
url: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: 'link' | 'photo' | 'video' | 'rich';
|
||||
author_name: string;
|
||||
author_url: string;
|
||||
provider_name: string;
|
||||
provider_url: string;
|
||||
html: string;
|
||||
width: number;
|
||||
height: number;
|
||||
image: string | null;
|
||||
embed_url: string;
|
||||
blurhash: string | null;
|
||||
}
|
||||
|
||||
async function unfurlCard(url: string, signal: AbortSignal): Promise<PreviewCard | null> {
|
||||
debug(`Unfurling ${url}...`);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { escape } from 'entities';
|
|||
import { nip19, UnsignedEvent } from 'nostr-tools';
|
||||
|
||||
import { Conf } from '@/config.ts';
|
||||
import { MastodonAccount } from '@/entities/MastodonAccount.ts';
|
||||
import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||
import { getLnurl } from '@/utils/lnurl.ts';
|
||||
import { nip05Cache } from '@/utils/nip05.ts';
|
||||
|
|
@ -17,10 +18,17 @@ interface ToAccountOpts {
|
|||
async function renderAccount(
|
||||
event: Omit<DittoEvent, 'id' | 'sig'>,
|
||||
opts: ToAccountOpts = {},
|
||||
) {
|
||||
): Promise<MastodonAccount> {
|
||||
const { withSource = false } = opts;
|
||||
const { pubkey } = event;
|
||||
|
||||
const names = getTagSet(event.user?.tags ?? [], 'n');
|
||||
if (names.has('disabled') || names.has('suspended')) {
|
||||
const account = await accountFromPubkey(pubkey, opts);
|
||||
account.pleroma.deactivated = true;
|
||||
return account;
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
nip05,
|
||||
|
|
@ -34,7 +42,6 @@ async function renderAccount(
|
|||
|
||||
const npub = nip19.npubEncode(pubkey);
|
||||
const parsed05 = await parseAndVerifyNip05(nip05, pubkey);
|
||||
const names = getTagSet(event.user?.tags ?? [], 'n');
|
||||
|
||||
return {
|
||||
id: pubkey,
|
||||
|
|
@ -77,6 +84,7 @@ async function renderAccount(
|
|||
accepts_zaps: Boolean(getLnurl({ lud06, lud16 })),
|
||||
},
|
||||
pleroma: {
|
||||
deactivated: names.has('disabled') || names.has('suspended'),
|
||||
is_admin: names.has('admin'),
|
||||
is_moderator: names.has('admin') || names.has('moderator'),
|
||||
is_suggested: names.has('suggested'),
|
||||
|
|
@ -92,7 +100,7 @@ async function renderAccount(
|
|||
};
|
||||
}
|
||||
|
||||
function accountFromPubkey(pubkey: string, opts: ToAccountOpts = {}) {
|
||||
function accountFromPubkey(pubkey: string, opts: ToAccountOpts = {}): Promise<MastodonAccount> {
|
||||
const event: UnsignedEvent = {
|
||||
kind: 0,
|
||||
pubkey,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { NostrEvent } from '@nostrify/nostrify';
|
|||
import { nip19 } from 'nostr-tools';
|
||||
|
||||
import { Conf } from '@/config.ts';
|
||||
import { MastodonMention } from '@/entities/MastodonMention.ts';
|
||||
import { MastodonStatus } from '@/entities/MastodonStatus.ts';
|
||||
import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||
import { Storages } from '@/storages.ts';
|
||||
import { nostrDate } from '@/utils.ts';
|
||||
|
|
@ -17,7 +19,7 @@ interface RenderStatusOpts {
|
|||
depth?: number;
|
||||
}
|
||||
|
||||
async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise<any> {
|
||||
async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise<MastodonStatus | undefined> {
|
||||
const { viewerPubkey, depth = 1 } = opts;
|
||||
|
||||
if (depth > 2 || depth < 0) return;
|
||||
|
|
@ -130,12 +132,14 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise<
|
|||
};
|
||||
}
|
||||
|
||||
async function renderReblog(event: DittoEvent, opts: RenderStatusOpts) {
|
||||
async function renderReblog(event: DittoEvent, opts: RenderStatusOpts): Promise<MastodonStatus | undefined> {
|
||||
const { viewerPubkey } = opts;
|
||||
if (!event.repost) return;
|
||||
|
||||
const status = await renderStatus(event, {}); // omit viewerPubkey intentionally
|
||||
const reblog = await renderStatus(event.repost, { viewerPubkey });
|
||||
if (!status) return;
|
||||
|
||||
const reblog = await renderStatus(event.repost, { viewerPubkey }) ?? null;
|
||||
|
||||
return {
|
||||
...status,
|
||||
|
|
@ -145,7 +149,7 @@ async function renderReblog(event: DittoEvent, opts: RenderStatusOpts) {
|
|||
};
|
||||
}
|
||||
|
||||
async function toMention(pubkey: string, event?: NostrEvent) {
|
||||
async function toMention(pubkey: string, event?: NostrEvent): Promise<MastodonMention> {
|
||||
const account = event ? await renderAccount(event) : undefined;
|
||||
|
||||
if (account) {
|
||||
|
|
@ -166,9 +170,7 @@ async function toMention(pubkey: string, event?: NostrEvent) {
|
|||
}
|
||||
}
|
||||
|
||||
type Mention = Awaited<ReturnType<typeof toMention>>;
|
||||
|
||||
function buildInlineRecipients(mentions: Mention[]): string {
|
||||
function buildInlineRecipients(mentions: MastodonMention[]): string {
|
||||
if (!mentions.length) return '';
|
||||
|
||||
const elements = mentions.reduce<string[]>((acc, { url, username }) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue