diff --git a/packages/backend/src/server/api/mastodon/converters/mention.ts b/packages/backend/src/server/api/mastodon/converters/mention.ts index edf200f79..ea9fbdffd 100644 --- a/packages/backend/src/server/api/mastodon/converters/mention.ts +++ b/packages/backend/src/server/api/mastodon/converters/mention.ts @@ -1,17 +1,19 @@ import { User } from "@/models/entities/user.js"; import config from "@/config/index.js"; -export function convertMention(u: User): MastodonEntity.Mention { - let acct = u.username; - let acctUrl = `https://${u.host || config.host}/@${u.username}`; - if (u.host) { - acct = `${u.username}@${u.host}`; - acctUrl = `https://${u.host}/@${u.username}`; - } - return { - id: u.id, - username: u.username, - acct: acct, - url: u.uri ?? acctUrl, - }; +export class MentionConverter { + public static encode(u: User): MastodonEntity.Mention { + let acct = u.username; + let acctUrl = `https://${u.host || config.host}/@${u.username}`; + if (u.host) { + acct = `${u.username}@${u.host}`; + acctUrl = `https://${u.host}/@${u.username}`; + } + return { + id: u.id, + username: u.username, + acct: acct, + url: u.uri ?? acctUrl, + }; + } } diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts index b2d8940f6..297a8dfe3 100644 --- a/packages/backend/src/server/api/mastodon/converters/note.ts +++ b/packages/backend/src/server/api/mastodon/converters/note.ts @@ -4,14 +4,14 @@ import { Note } from "@/models/entities/note.js"; import config from "@/config/index.js"; import mfm from "mfm-js"; import { toHtml } from "@/mfm/to-html.js"; -import { convertUser } from "@/server/api/mastodon/converters/user.js"; -import { Visibility } from "@/server/api/mastodon/converters/visibility.js"; +import { UserConverter } from "@/server/api/mastodon/converters/user.js"; +import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js"; import { escapeMFM } from "@/server/api/mastodon/converters/mfm.js"; import { populateEmojis } from "@/misc/populate-emojis.js"; import { EmojiConverter } from "@/server/api/mastodon/converters/emoji.js"; import { DriveFiles, NoteFavorites, NoteReactions, Notes, NoteThreadMutings } from "@/models/index.js"; import { decodeReaction } from "@/misc/reaction-lib.js"; -import { convertMention } from "@/server/api/mastodon/converters/mention.js"; +import { MentionConverter } from "@/server/api/mastodon/converters/mention.js"; import { PollConverter } from "@/server/api/mastodon/converters/poll.js"; import { populatePoll } from "@/models/repositories/note.js"; import { FileConverter } from "@/server/api/mastodon/converters/file.js"; @@ -74,7 +74,7 @@ export class NoteConverter { id: note.id, uri: note.uri ? note.uri : `https://${config.host}/notes/${note.id}`, url: note.uri ? note.uri : `https://${config.host}/notes/${note.id}`, - account: await convertUser(noteUser), + account: await UserConverter.encode(noteUser), in_reply_to_id: note.replyId, in_reply_to_account_id: reply?.userId ?? null, reblog: note.renote ? await this.encode(note.renote, user) : null, @@ -93,9 +93,9 @@ export class NoteConverter { muted: isMuted, sensitive: files.length > 0 ? files.some((f) => f.isSensitive) : false, spoiler_text: note.cw ? note.cw : "", - visibility: Visibility.encode(note.visibility), + visibility: VisibilityConverter.encode(note.visibility), media_attachments: files.length > 0 ? files.map((f) => FileConverter.encode(f)) : [], - mentions: await Promise.all(note.mentions.map(async p => convertMention(await getUser(p)))), + mentions: await Promise.all(note.mentions.map(async p => MentionConverter.encode(await getUser(p)))), tags: [], //FIXME card: null, //FIXME poll: note.hasPoll ? PollConverter.encode(await populatePoll(note, user.id), note.id) : null, diff --git a/packages/backend/src/server/api/mastodon/converters/user.ts b/packages/backend/src/server/api/mastodon/converters/user.ts index 38a24e14b..b90ccef25 100644 --- a/packages/backend/src/server/api/mastodon/converters/user.ts +++ b/packages/backend/src/server/api/mastodon/converters/user.ts @@ -13,43 +13,46 @@ type Field = { verified?: boolean; }; -export async function convertUser(u: User): Promise { - let acct = u.username; - let acctUrl = `https://${u.host || config.host}/@${u.username}`; - if (u.host) { - acct = `${u.username}@${u.host}`; - acctUrl = `https://${u.host}/@${u.username}`; - } +export class UserConverter { + public static async encode(u: User): Promise { + let acct = u.username; + let acctUrl = `https://${u.host || config.host}/@${u.username}`; + if (u.host) { + acct = `${u.username}@${u.host}`; + acctUrl = `https://${u.host}/@${u.username}`; + } const profile = await UserProfiles.findOneBy({userId: u.id}); const bio = toHtml(mfm.parse(profile?.description ?? "")) ?? escapeMFM(profile?.description ?? ""); - return { - id: u.id, - username: u.username, - acct: acct, - display_name: u.name || u.username, - locked: u.isLocked, - created_at: new Date().toISOString(), - followers_count: u.followersCount, - following_count: u.followingCount, - statuses_count: u.notesCount, - note: bio, - url: u.uri ?? acctUrl, - avatar: u.avatar?.url ?? Users.getIdenticonUrl(u.id), - avatar_static: u.avatar?.url ?? Users.getIdenticonUrl(u.id), - header: u.banner?.url ?? `${config.url}/static-assets/transparent.png`, - header_static: u.banner?.url ?? `${config.url}/static-assets/transparent.png`, - emojis: (await populateEmojis(u.emojis, u.host)).map((e) => EmojiConverter.encode(e)), - moved: null, //FIXME - fields: profile?.fields.map(p => convertField(p)) ?? [], - bot: u.isBot - }; -} - -function convertField(f: Field): MastodonEntity.Field { - return { - name: f.name, - value: toHtml(mfm.parse(f.value)) ?? escapeMFM(f.value), - verified_at: f.verified ? (new Date()).toISOString() : null, + return { + id: u.id, + username: u.username, + acct: acct, + display_name: u.name || u.username, + locked: u.isLocked, + created_at: new Date().toISOString(), + followers_count: u.followersCount, + following_count: u.followingCount, + statuses_count: u.notesCount, + note: bio, + url: u.uri ?? acctUrl, + avatar: u.avatar?.url ?? Users.getIdenticonUrl(u.id), + avatar_static: u.avatar?.url ?? Users.getIdenticonUrl(u.id), + header: u.banner?.url ?? `${config.url}/static-assets/transparent.png`, + header_static: u.banner?.url ?? `${config.url}/static-assets/transparent.png`, + emojis: (await populateEmojis(u.emojis, u.host)).map((e) => EmojiConverter.encode(e)), + moved: null, //FIXME + fields: profile?.fields.map(p => this.encodeField(p)) ?? [], + bot: u.isBot + }; } + + private static encodeField(f: Field): MastodonEntity.Field { + return { + name: f.name, + value: toHtml(mfm.parse(f.value)) ?? escapeMFM(f.value), + verified_at: f.verified ? (new Date()).toISOString() : null, + } + } + } diff --git a/packages/backend/src/server/api/mastodon/converters/visibility.ts b/packages/backend/src/server/api/mastodon/converters/visibility.ts index 6599d6cf7..cba1db656 100644 --- a/packages/backend/src/server/api/mastodon/converters/visibility.ts +++ b/packages/backend/src/server/api/mastodon/converters/visibility.ts @@ -1,7 +1,7 @@ type IceshrimpVisibility = "public" | "home" | "followers" | "specified" | "hidden"; type MastodonVisibility = "public" | "unlisted" | "private" | "direct"; -export class Visibility { +export class VisibilityConverter { public static encode (v: IceshrimpVisibility): MastodonVisibility { switch (v) { case "public": diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 3a7f5db2c..931801381 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -13,7 +13,7 @@ import { convertStatus, } from "../converters.js"; import { getNote, getUser } from "@/server/api/common/getters.js"; -import { convertUser } from "@/server/api/mastodon/converters/user.js"; +import { UserConverter } from "@/server/api/mastodon/converters/user.js"; const relationshipModel = { id: "", @@ -135,7 +135,7 @@ export function apiAccountMastodon(router: Router): void { router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => { try { const userId = convertId(ctx.params.id, IdType.IceshrimpId); - const account = await convertUser(await getUser(userId)); + const account = await UserConverter.encode(await getUser(userId)); ctx.body = convertAccount(account); } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 4458a7a1f..0532bda68 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -12,8 +12,8 @@ import { convertPoll, convertStatus, } from "../converters.js"; -import {NoteConverter} from "@/server/api/mastodon/converters/note.js"; -import {getNote} from "@/server/api/common/getters.js"; +import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; +import { getNote } from "@/server/api/common/getters.js"; import authenticate from "@/server/api/authenticate.js"; import {Notes} from "@/models";