mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Merge branch 'entity-types' into 'main'
Add interfaces for Mastodon entity types, hide deactivated accounts See merge request soapbox-pub/ditto!383
This commit is contained in:
commit
287c89b9fd
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 DOMPurify from 'isomorphic-dompurify';
|
||||||
import { unfurl } from 'unfurl.js';
|
import { unfurl } from 'unfurl.js';
|
||||||
|
|
||||||
|
import { PreviewCard } from '@/entities/PreviewCard.ts';
|
||||||
import { Time } from '@/utils/time.ts';
|
import { Time } from '@/utils/time.ts';
|
||||||
import { fetchWorker } from '@/workers/fetch.ts';
|
import { fetchWorker } from '@/workers/fetch.ts';
|
||||||
|
|
||||||
const debug = Debug('ditto:unfurl');
|
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> {
|
async function unfurlCard(url: string, signal: AbortSignal): Promise<PreviewCard | null> {
|
||||||
debug(`Unfurling ${url}...`);
|
debug(`Unfurling ${url}...`);
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { escape } from 'entities';
|
||||||
import { nip19, UnsignedEvent } from 'nostr-tools';
|
import { nip19, UnsignedEvent } from 'nostr-tools';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
import { MastodonAccount } from '@/entities/MastodonAccount.ts';
|
||||||
import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
import { getLnurl } from '@/utils/lnurl.ts';
|
import { getLnurl } from '@/utils/lnurl.ts';
|
||||||
import { nip05Cache } from '@/utils/nip05.ts';
|
import { nip05Cache } from '@/utils/nip05.ts';
|
||||||
|
|
@ -17,10 +18,17 @@ interface ToAccountOpts {
|
||||||
async function renderAccount(
|
async function renderAccount(
|
||||||
event: Omit<DittoEvent, 'id' | 'sig'>,
|
event: Omit<DittoEvent, 'id' | 'sig'>,
|
||||||
opts: ToAccountOpts = {},
|
opts: ToAccountOpts = {},
|
||||||
) {
|
): Promise<MastodonAccount> {
|
||||||
const { withSource = false } = opts;
|
const { withSource = false } = opts;
|
||||||
const { pubkey } = event;
|
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 {
|
const {
|
||||||
name,
|
name,
|
||||||
nip05,
|
nip05,
|
||||||
|
|
@ -34,7 +42,6 @@ async function renderAccount(
|
||||||
|
|
||||||
const npub = nip19.npubEncode(pubkey);
|
const npub = nip19.npubEncode(pubkey);
|
||||||
const parsed05 = await parseAndVerifyNip05(nip05, pubkey);
|
const parsed05 = await parseAndVerifyNip05(nip05, pubkey);
|
||||||
const names = getTagSet(event.user?.tags ?? [], 'n');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: pubkey,
|
id: pubkey,
|
||||||
|
|
@ -77,6 +84,7 @@ async function renderAccount(
|
||||||
accepts_zaps: Boolean(getLnurl({ lud06, lud16 })),
|
accepts_zaps: Boolean(getLnurl({ lud06, lud16 })),
|
||||||
},
|
},
|
||||||
pleroma: {
|
pleroma: {
|
||||||
|
deactivated: names.has('disabled') || names.has('suspended'),
|
||||||
is_admin: names.has('admin'),
|
is_admin: names.has('admin'),
|
||||||
is_moderator: names.has('admin') || names.has('moderator'),
|
is_moderator: names.has('admin') || names.has('moderator'),
|
||||||
is_suggested: names.has('suggested'),
|
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 = {
|
const event: UnsignedEvent = {
|
||||||
kind: 0,
|
kind: 0,
|
||||||
pubkey,
|
pubkey,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import { NostrEvent } from '@nostrify/nostrify';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
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 { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { nostrDate } from '@/utils.ts';
|
import { nostrDate } from '@/utils.ts';
|
||||||
|
|
@ -17,7 +19,7 @@ interface RenderStatusOpts {
|
||||||
depth?: number;
|
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;
|
const { viewerPubkey, depth = 1 } = opts;
|
||||||
|
|
||||||
if (depth > 2 || depth < 0) return;
|
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;
|
const { viewerPubkey } = opts;
|
||||||
if (!event.repost) return;
|
if (!event.repost) return;
|
||||||
|
|
||||||
const status = await renderStatus(event, {}); // omit viewerPubkey intentionally
|
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 {
|
return {
|
||||||
...status,
|
...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;
|
const account = event ? await renderAccount(event) : undefined;
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
|
|
@ -166,9 +170,7 @@ async function toMention(pubkey: string, event?: NostrEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mention = Awaited<ReturnType<typeof toMention>>;
|
function buildInlineRecipients(mentions: MastodonMention[]): string {
|
||||||
|
|
||||||
function buildInlineRecipients(mentions: Mention[]): string {
|
|
||||||
if (!mentions.length) return '';
|
if (!mentions.length) return '';
|
||||||
|
|
||||||
const elements = mentions.reduce<string[]>((acc, { url, username }) => {
|
const elements = mentions.reduce<string[]>((acc, { url, username }) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue