From 82c10de2651107cb19c788d756cdeeb84122c0b0 Mon Sep 17 00:00:00 2001 From: Mar0xy Date: Sun, 29 Oct 2023 00:50:00 +0200 Subject: [PATCH] upd: change deps, fix a few bugs, update converter Fixes User and Notes count bug (transfem-org/Sharkey#113) Fixes build issues due to types (transfem-org/Sharkey#111) Return accounts and notes like Iceshrimp Use MFM class from Iceshrimp to fix HTML output for mastodon --- packages/backend/src/core/MfmService.ts | 208 ++++++++++++++++++ .../api/mastodon/MastodonApiServerService.ts | 44 ++-- .../src/server/api/mastodon/converters.ts | 177 ++++++++------- .../server/api/mastodon/endpoints/account.ts | 73 +++--- .../server/api/mastodon/endpoints/filter.ts | 8 +- .../api/mastodon/endpoints/notifications.ts | 9 +- .../server/api/mastodon/endpoints/search.ts | 6 +- .../server/api/mastodon/endpoints/status.ts | 67 +++--- .../server/api/mastodon/endpoints/timeline.ts | 44 ++-- packages/megalodon/package.json | 8 +- packages/megalodon/src/entities/account.ts | 9 +- packages/megalodon/src/entities/status.ts | 2 +- packages/megalodon/src/misskey.ts | 2 +- packages/megalodon/src/misskey/api_client.ts | 6 +- 14 files changed, 421 insertions(+), 242 deletions(-) diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index b275d1b14..91690aec1 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -388,4 +388,212 @@ export class MfmService { return `

${doc.body.innerHTML}

`; } + + @bindThis + public async toMastoHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = [], inline = false, quoteUri: string | null = null) { + if (nodes == null) { + return null; + } + + const { window } = new Window(); + + const doc = window.document; + + async function appendChildren(children: mfm.MfmNode[], targetElement: any): Promise { + if (children) { + for (const child of await Promise.all(children.map(async (x) => await (handlers as any)[x.type](x)))) targetElement.appendChild(child); + } + } + + const handlers: { + [K in mfm.MfmNode['type']]: (node: mfm.NodeType) => any; + } = { + async bold(node) { + const el = doc.createElement('span'); + el.textContent = '**'; + await appendChildren(node.children, el); + el.textContent += '**'; + return el; + }, + + async small(node) { + const el = doc.createElement('small'); + await appendChildren(node.children, el); + return el; + }, + + async strike(node) { + const el = doc.createElement('span'); + el.textContent = '~~'; + await appendChildren(node.children, el); + el.textContent += '~~'; + return el; + }, + + async italic(node) { + const el = doc.createElement('span'); + el.textContent = '*'; + await appendChildren(node.children, el); + el.textContent += '*'; + return el; + }, + + async fn(node) { + const el = doc.createElement('span'); + el.textContent = '*'; + await appendChildren(node.children, el); + el.textContent += '*'; + return el; + }, + + blockCode(node) { + const pre = doc.createElement('pre'); + const inner = doc.createElement('code'); + + const nodes = node.props.code + .split(/\r\n|\r|\n/) + .map((x) => doc.createTextNode(x)); + + for (const x of intersperse('br', nodes)) { + inner.appendChild(x === 'br' ? doc.createElement('br') : x); + } + + pre.appendChild(inner); + return pre; + }, + + async center(node) { + const el = doc.createElement('div'); + await appendChildren(node.children, el); + return el; + }, + + emojiCode(node) { + return doc.createTextNode(`\u200B:${node.props.name}:\u200B`); + }, + + unicodeEmoji(node) { + return doc.createTextNode(node.props.emoji); + }, + + hashtag: (node) => { + const a = doc.createElement('a'); + a.setAttribute('href', `${this.config.url}/tags/${node.props.hashtag}`); + a.textContent = `#${node.props.hashtag}`; + a.setAttribute('rel', 'tag'); + a.setAttribute('class', 'hashtag'); + return a; + }, + + inlineCode(node) { + const el = doc.createElement('code'); + el.textContent = node.props.code; + return el; + }, + + mathInline(node) { + const el = doc.createElement('code'); + el.textContent = node.props.formula; + return el; + }, + + mathBlock(node) { + const el = doc.createElement('code'); + el.textContent = node.props.formula; + return el; + }, + + async link(node) { + const a = doc.createElement('a'); + a.setAttribute('rel', 'nofollow noopener noreferrer'); + a.setAttribute('target', '_blank'); + a.setAttribute('href', node.props.url); + await appendChildren(node.children, a); + return a; + }, + + async mention(node) { + const { username, host, acct } = node.props; + const resolved = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host); + + const el = doc.createElement('span'); + if (!resolved) { + el.textContent = acct; + } else { + el.setAttribute('class', 'h-card'); + el.setAttribute('translate', 'no'); + const a = doc.createElement('a'); + a.setAttribute('href', resolved.url ? resolved.url : resolved.uri); + a.className = 'u-url mention'; + const span = doc.createElement('span'); + span.textContent = resolved.username || username; + a.textContent = '@'; + a.appendChild(span); + el.appendChild(a); + } + + return el; + }, + + async quote(node) { + const el = doc.createElement('blockquote'); + await appendChildren(node.children, el); + return el; + }, + + text(node) { + const el = doc.createElement('span'); + const nodes = node.props.text + .split(/\r\n|\r|\n/) + .map((x) => doc.createTextNode(x)); + + for (const x of intersperse('br', nodes)) { + el.appendChild(x === 'br' ? doc.createElement('br') : x); + } + + return el; + }, + + url(node) { + const a = doc.createElement('a'); + a.setAttribute('rel', 'nofollow noopener noreferrer'); + a.setAttribute('target', '_blank'); + a.setAttribute('href', node.props.url); + a.textContent = node.props.url.replace(/^https?:\/\//, ''); + return a; + }, + + search: (node) => { + const a = doc.createElement('a'); + a.setAttribute('href', `https"google.com/${node.props.query}`); + a.textContent = node.props.content; + return a; + }, + + async plain(node) { + const el = doc.createElement('span'); + await appendChildren(node.children, el); + return el; + }, + }; + + await appendChildren(nodes, doc.body); + + if (quoteUri !== null) { + const a = doc.createElement('a'); + a.setAttribute('href', quoteUri); + a.textContent = quoteUri.replace(/^https?:\/\//, ''); + + const quote = doc.createElement('span'); + quote.setAttribute('class', 'quote-inline'); + quote.appendChild(doc.createElement('br')); + quote.appendChild(doc.createElement('br')); + quote.innerHTML += 'RE: '; + quote.appendChild(a); + + doc.body.appendChild(quote); + } + + return inline ? doc.body.innerHTML : `

${doc.body.innerHTML}

`; + } } diff --git a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts index a8c45b98f..9a2890f50 100644 --- a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts +++ b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts @@ -8,7 +8,7 @@ import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; -import { convertId, IdConvertType as IdType, convertAccount, convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList } from './converters.js'; +import { convertAccount, convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList } from './converters.js'; import { getInstance } from './endpoints/meta.js'; import { ApiAuthMastodon, ApiAccountMastodon, ApiFilterMastodon, ApiNotifyMastodon, ApiSearchMastodon, ApiTimelineMastodon, ApiStatusMastodon } from './endpoints.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -128,7 +128,7 @@ export class MastodonApiServerService { const client = getClient(BASE_URL, accessTokens); try { const data = await client.dismissInstanceAnnouncement( - convertId(_request.body['id'], IdType.SharkeyId), + _request.body['id'], ); reply.send(data.data); } catch (e: any) { @@ -236,7 +236,7 @@ export class MastodonApiServerService { const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt // displayed without being logged in try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.verifyCredentials()); } catch (e: any) { /* console.error(e); */ @@ -286,7 +286,7 @@ export class MastodonApiServerService { ids = [ids]; } users = ids; - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getRelationships(users)); } catch (e: any) { /* console.error(e); */ @@ -302,7 +302,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const sharkId = convertId(_request.params.id, IdType.SharkeyId); + const sharkId = _request.params.id; const data = await client.getAccount(sharkId); const profile = await this.userProfilesRepository.findOneBy({ userId: sharkId }); data.data.fields = profile?.fields.map(f => ({ ...f, verified_at: null })) || []; @@ -319,7 +319,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getStatuses()); } catch (e: any) { /* console.error(e); @@ -347,7 +347,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getFollowers()); } catch (e: any) { /* console.error(e); @@ -361,7 +361,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getFollowing()); } catch (e: any) { /* console.error(e); @@ -375,7 +375,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getAccountLists(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.getAccountLists(_request.params.id); reply.send(data.data.map((list) => convertList(list))); } catch (e: any) { /* console.error(e); @@ -389,7 +389,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.addFollow()); } catch (e: any) { /* console.error(e); @@ -403,7 +403,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.rmFollow()); } catch (e: any) { /* console.error(e); @@ -417,7 +417,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.addBlock()); } catch (e: any) { /* console.error(e); @@ -431,7 +431,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.rmBlock()); } catch (e: any) { /* console.error(e); @@ -445,7 +445,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.addMute()); } catch (e: any) { /* console.error(e); @@ -459,7 +459,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.rmMute()); } catch (e: any) { /* console.error(e); @@ -487,7 +487,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getBookmarks()); } catch (e: any) { /* console.error(e); @@ -501,7 +501,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getFavourites()); } catch (e: any) { /* console.error(e); @@ -515,7 +515,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getMutes()); } catch (e: any) { /* console.error(e); @@ -529,7 +529,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.getBlocks()); } catch (e: any) { /* console.error(e); @@ -557,7 +557,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.acceptFollow()); } catch (e: any) { /* console.error(e); @@ -571,7 +571,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const account = new ApiAccountMastodon(_request, client, BASE_URL); + const account = new ApiAccountMastodon(_request, client, BASE_URL, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); reply.send(await account.rejectFollow()); } catch (e: any) { /* console.error(e); @@ -813,7 +813,7 @@ export class MastodonApiServerService { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.updateMedia(convertId(_request.params.id, IdType.SharkeyId), _request.body!); + const data = await client.updateMedia(_request.params.id, _request.body!); reply.send(convertAttachment(data.data)); } catch (e: any) { /* console.error(e); */ diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts index cbd2550f9..a83914560 100644 --- a/packages/backend/src/server/api/mastodon/converters.ts +++ b/packages/backend/src/server/api/mastodon/converters.ts @@ -3,14 +3,13 @@ import { MfmService } from '@/core/MfmService.js'; import { DI } from '@/di-symbols.js'; import { Inject } from '@nestjs/common'; import { Entity } from 'megalodon'; -import { parse } from 'mfm-js'; +import mfm from 'mfm-js'; import { GetterService } from '../GetterService.js'; import type { IMentionedRemoteUsers } from '@/models/Note.js'; import type { MiUser } from '@/models/User.js'; import type { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; - -const CHAR_COLLECTION = '0123456789abcdefghijklmnopqrstuvwxyz'; +import { awaitAll } from '@/misc/prelude/await-all.js'; export enum IdConvertType { MastodonId, @@ -18,13 +17,13 @@ export enum IdConvertType { } export const escapeMFM = (text: string): string => text - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'") - .replace(/`/g, "`") - .replace(/\r?\n/g, "
"); + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(/`/g, "`") + .replace(/\r?\n/g, "
"); export class MastoConverters { private MfmService: MfmService; @@ -49,7 +48,7 @@ export class MastoConverters { this.GetterService = new GetterService(this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService); } - private encode(u: MiUser, m: IMentionedRemoteUsers): MastodonEntity.Mention { + private encode(u: MiUser, m: IMentionedRemoteUsers): Entity.Mention { let acct = u.username; let acctUrl = `https://${u.host || this.config.host}/@${u.username}`; let url: string | null = null; @@ -73,89 +72,97 @@ export class MastoConverters { }); } + public async convertAccount(account: Entity.Account) { + return awaitAll({ + id: account.id, + username: account.username, + acct: account.acct, + fqn: account.fqn, + display_name: account.display_name || account.username, + locked: account.locked, + created_at: account.created_at, + followers_count: account.followers_count, + following_count: account.following_count, + statuses_count: account.statuses_count, + note: account.note, + url: account.url, + avatar: account.avatar, + avatar_static: account.avatar, + header: account.header, + header_static: account.header, + emojis: account.emojis, + moved: null, //FIXME + fields: [], + bot: false, + discoverable: true, + }); + } + public async convertStatus(status: Entity.Status) { - status.account = convertAccount(status.account); + const convertedAccount = this.convertAccount(status.account); const note = await this.GetterService.getNote(status.id); - status.id = convertId(status.id, IdConvertType.MastodonId); - if (status.in_reply_to_account_id) status.in_reply_to_account_id = convertId( - status.in_reply_to_account_id, - IdConvertType.MastodonId, - ); - if (status.in_reply_to_id) status.in_reply_to_id = convertId(status.in_reply_to_id, IdConvertType.MastodonId); - status.media_attachments = status.media_attachments.map((attachment) => - convertAttachment(attachment), - ); - // This will eventually be improved with a rewrite of this file + const mentions = Promise.all(note.mentions.map(p => this.getUser(p) .then(u => this.encode(u, JSON.parse(note.mentionedRemoteUsers))) .catch(() => null))) - .then(p => p.filter(m => m)) as Promise; - status.mentions = await mentions; - status.mentions = status.mentions.map((mention) => ({ - ...mention, - id: convertId(mention.id, IdConvertType.MastodonId), - })); - const convertedMFM = this.MfmService.toHtml(parse(status.content), JSON.parse(note.mentionedRemoteUsers)); - status.content = status.content ? convertedMFM?.replace(/&/g, "&").replaceAll(`&`, "\'") as string : status.content; - if (status.poll) status.poll = convertPoll(status.poll); - if (status.reblog) status.reblog = convertStatus(status.reblog); - - return status; - } -} + .then(p => p.filter(m => m)) as Promise; -export function convertId(in_id: string, id_convert_type: IdConvertType): string { - switch (id_convert_type) { - case IdConvertType.MastodonId: { - let out = BigInt(0); - const lowerCaseId = in_id.toLowerCase(); - for (let i = 0; i < lowerCaseId.length; i++) { - const charValue = numFromChar(lowerCaseId.charAt(i)); - out += BigInt(charValue) * BigInt(36) ** BigInt(i); - } - return out.toString(); - } - - case IdConvertType.SharkeyId: { - let input = BigInt(in_id); - let outStr = ''; - while (input > BigInt(0)) { - const remainder = Number(input % BigInt(36)); - outStr = charFromNum(remainder) + outStr; - input /= BigInt(36); - } - const ReversedoutStr = outStr.split('').reduce((acc, char) => char + acc, ''); - return ReversedoutStr; - } - - default: - throw new Error('Invalid ID conversion type'); - } -} + const content = note.text !== null + ? this.MfmService.toMastoHtml(mfm.parse(note.text!), JSON.parse(note.mentionedRemoteUsers), false, null) + .then(p => p ?? escapeMFM(note.text!)) + : ''; -function numFromChar(character: string): number { - for (let i = 0; i < CHAR_COLLECTION.length; i++) { - if (CHAR_COLLECTION.charAt(i) === character) { - return i; - } - } + const tags = note.tags.map(tag => { + return { + name: tag, + url: `${this.config.url}/tags/${tag}`, + } as Entity.Tag; + }); - throw new Error('Invalid character in parsed base36 id'); -} - -function charFromNum(number: number): string { - if (number >= 0 && number < CHAR_COLLECTION.length) { - return CHAR_COLLECTION.charAt(number); - } else { - throw new Error('Invalid number for base-36 encoding'); + // noinspection ES6MissingAwait + return await awaitAll({ + id: note.id, + uri: note.uri ?? `https://${this.config.host}/notes/${note.id}`, + url: note.url ?? note.uri ?? `https://${this.config.host}/notes/${note.id}`, + account: convertedAccount, + in_reply_to_id: note.replyId, + in_reply_to_account_id: note.replyUserId, + reblog: status.reblog, + content: content, + content_type: 'text/x.misskeymarkdown', + text: note.text, + created_at: status.created_at, + emojis: status.emojis, + replies_count: note.repliesCount, + reblogs_count: note.renoteCount, + favourites_count: status.favourites_count, + reblogged: false, + favourited: status.favourited, + muted: status.muted, + sensitive: status.sensitive, + spoiler_text: note.cw ? note.cw : '', + visibility: status.visibility, + media_attachments: status.media_attachments, + mentions: mentions, + tags: tags, + card: null, //FIXME + poll: status.poll ?? null, + application: null, //FIXME + language: null, //FIXME + pinned: null, + reactions: status.emoji_reactions, + emoji_reactions: status.emoji_reactions, + bookmarked: false, + quote: false, + edited_at: note.updatedAt?.toISOString(), + }); } } function simpleConvert(data: any) { // copy the object to bypass weird pass by reference bugs const result = Object.assign({}, data); - result.id = convertId(data.id, IdConvertType.MastodonId); return result; } @@ -180,7 +187,6 @@ export function convertFeaturedTag(tag: Entity.FeaturedTag) { export function convertNotification(notification: Entity.Notification) { notification.account = convertAccount(notification.account); - notification.id = convertId(notification.id, IdConvertType.MastodonId); if (notification.status) notification.status = convertStatus(notification.status); return notification; } @@ -200,19 +206,9 @@ export function convertRelationship(relationship: Entity.Relationship) { export function convertStatus(status: Entity.Status) { status.account = convertAccount(status.account); - status.id = convertId(status.id, IdConvertType.MastodonId); - if (status.in_reply_to_account_id) status.in_reply_to_account_id = convertId( - status.in_reply_to_account_id, - IdConvertType.MastodonId, - ); - if (status.in_reply_to_id) status.in_reply_to_id = convertId(status.in_reply_to_id, IdConvertType.MastodonId); status.media_attachments = status.media_attachments.map((attachment) => convertAttachment(attachment), ); - status.mentions = status.mentions.map((mention) => ({ - ...mention, - id: convertId(mention.id, IdConvertType.MastodonId), - })); if (status.poll) status.poll = convertPoll(status.poll); if (status.reblog) status.reblog = convertStatus(status.reblog); @@ -224,7 +220,6 @@ export function convertStatusSource(status: Entity.StatusSource) { } export function convertConversation(conversation: Entity.Conversation) { - conversation.id = convertId(conversation.id, IdConvertType.MastodonId); conversation.accounts = conversation.accounts.map(convertAccount); if (conversation.last_status) { conversation.last_status = convertStatus(conversation.last_status); diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 24ebe0c48..802f1f5a6 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -1,7 +1,10 @@ -import { convertId, IdConvertType as IdType, convertAccount, convertRelationship, convertStatus } from '../converters.js'; -import { argsToBools, convertTimelinesArgsId, limitToInt } from './timeline.js'; +import { MastoConverters, convertRelationship } from '../converters.js'; +import { argsToBools, limitToInt } from './timeline.js'; import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; +import { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import type { Config } from '@/config.js'; const relationshipModel = { id: '', @@ -24,18 +27,25 @@ export class ApiAccountMastodon { private request: FastifyRequest; private client: MegalodonInterface; private BASE_URL: string; + private mastoconverter: MastoConverters; - constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string) { + constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string, + config: Config, + usersrepo: UsersRepository, + notesrepo: NotesRepository, + noteeditrepo: NoteEditRepository, + userentity: UserEntityService, + ) { this.request = request; this.client = client; this.BASE_URL = BASE_URL; + this.mastoconverter = new MastoConverters(config, usersrepo, notesrepo, noteeditrepo, userentity); } public async verifyCredentials() { try { const data = await this.client.verifyAccountCredentials(); const acct = data.data; - acct.id = convertId(acct.id, IdType.MastodonId); acct.display_name = acct.display_name || acct.username; acct.url = `${this.BASE_URL}/@${acct.url}`; acct.note = acct.note || ''; @@ -61,7 +71,7 @@ export class ApiAccountMastodon { public async lookup() { try { const data = await this.client.search((this.request.query as any).acct, { type: 'accounts' }); - return convertAccount(data.data.accounts[0]); + return this.mastoconverter.convertAccount(data.data.accounts[0]); } catch (e: any) { /* console.error(e) console.error(e.response.data); */ @@ -79,7 +89,7 @@ export class ApiAccountMastodon { const reqIds = []; for (let i = 0; i < users.length; i++) { - reqIds.push(convertId(users[i], IdType.SharkeyId)); + reqIds.push(users[i]); } const data = await this.client.getRelationships(reqIds); @@ -93,11 +103,8 @@ export class ApiAccountMastodon { public async getStatuses() { try { - const data = await this.client.getAccountStatuses( - convertId((this.request.params as any).id, IdType.SharkeyId), - convertTimelinesArgsId(argsToBools(limitToInt(this.request.query as any))) - ); - return data.data.map((status) => convertStatus(status)); + const data = await this.client.getAccountStatuses((this.request.params as any).id, argsToBools(limitToInt(this.request.query as any))); + return data.data.map((status) => this.mastoconverter.convertStatus(status)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -108,10 +115,10 @@ export class ApiAccountMastodon { public async getFollowers() { try { const data = await this.client.getAccountFollowers( - convertId((this.request.params as any).id, IdType.SharkeyId), - convertTimelinesArgsId(limitToInt(this.request.query as any)), + (this.request.params as any).id, + limitToInt(this.request.query as any), ); - return data.data.map((account) => convertAccount(account)); + return data.data.map((account) => this.mastoconverter.convertAccount(account)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -122,10 +129,10 @@ export class ApiAccountMastodon { public async getFollowing() { try { const data = await this.client.getAccountFollowing( - convertId((this.request.params as any).id, IdType.SharkeyId), - convertTimelinesArgsId(limitToInt(this.request.query as any)), + (this.request.params as any).id, + limitToInt(this.request.query as any), ); - return data.data.map((account) => convertAccount(account)); + return data.data.map((account) => this.mastoconverter.convertAccount(account)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -135,7 +142,7 @@ export class ApiAccountMastodon { public async addFollow() { try { - const data = await this.client.followAccount( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.followAccount( (this.request.params as any).id ); const acct = convertRelationship(data.data); acct.following = true; return acct; @@ -148,7 +155,7 @@ export class ApiAccountMastodon { public async rmFollow() { try { - const data = await this.client.unfollowAccount( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.unfollowAccount( (this.request.params as any).id ); const acct = convertRelationship(data.data); acct.following = false; return acct; @@ -161,7 +168,7 @@ export class ApiAccountMastodon { public async addBlock() { try { - const data = await this.client.blockAccount( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.blockAccount( (this.request.params as any).id ); return convertRelationship(data.data); } catch (e: any) { console.error(e); @@ -172,7 +179,7 @@ export class ApiAccountMastodon { public async rmBlock() { try { - const data = await this.client.unblockAccount( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.unblockAccount( (this.request.params as any).id ); return convertRelationship(data.data); } catch (e: any) { console.error(e); @@ -184,7 +191,7 @@ export class ApiAccountMastodon { public async addMute() { try { const data = await this.client.muteAccount( - convertId((this.request.params as any).id, IdType.SharkeyId), + (this.request.params as any).id, this.request.body as any, ); return convertRelationship(data.data); @@ -197,7 +204,7 @@ export class ApiAccountMastodon { public async rmMute() { try { - const data = await this.client.unmuteAccount( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.unmuteAccount( (this.request.params as any).id ); return convertRelationship(data.data); } catch (e: any) { console.error(e); @@ -208,8 +215,8 @@ export class ApiAccountMastodon { public async getBookmarks() { try { - const data = await this.client.getBookmarks( convertTimelinesArgsId(limitToInt(this.request.query as any)) ); - return data.data.map((status) => convertStatus(status)); + const data = await this.client.getBookmarks( limitToInt(this.request.query as any) ); + return data.data.map((status) => this.mastoconverter.convertStatus(status)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -219,8 +226,8 @@ export class ApiAccountMastodon { public async getFavourites() { try { - const data = await this.client.getFavourites( convertTimelinesArgsId(limitToInt(this.request.query as any)) ); - return data.data.map((status) => convertStatus(status)); + const data = await this.client.getFavourites( limitToInt(this.request.query as any) ); + return data.data.map((status) => this.mastoconverter.convertStatus(status)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -230,8 +237,8 @@ export class ApiAccountMastodon { public async getMutes() { try { - const data = await this.client.getMutes( convertTimelinesArgsId(limitToInt(this.request.query as any)) ); - return data.data.map((account) => convertAccount(account)); + const data = await this.client.getMutes( limitToInt(this.request.query as any) ); + return data.data.map((account) => this.mastoconverter.convertAccount(account)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -241,8 +248,8 @@ export class ApiAccountMastodon { public async getBlocks() { try { - const data = await this.client.getBlocks( convertTimelinesArgsId(limitToInt(this.request.query as any)) ); - return data.data.map((account) => convertAccount(account)); + const data = await this.client.getBlocks( limitToInt(this.request.query as any) ); + return data.data.map((account) => this.mastoconverter.convertAccount(account)); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -252,7 +259,7 @@ export class ApiAccountMastodon { public async acceptFollow() { try { - const data = await this.client.acceptFollowRequest( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.acceptFollowRequest( (this.request.params as any).id ); return convertRelationship(data.data); } catch (e: any) { console.error(e); @@ -263,7 +270,7 @@ export class ApiAccountMastodon { public async rejectFollow() { try { - const data = await this.client.rejectFollowRequest( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.rejectFollowRequest( (this.request.params as any).id ); return convertRelationship(data.data); } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/filter.ts b/packages/backend/src/server/api/mastodon/endpoints/filter.ts index e27bc956f..212c79b25 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/filter.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/filter.ts @@ -1,4 +1,4 @@ -import { IdConvertType as IdType, convertId, convertFilter } from '../converters.js'; +import { convertFilter } from '../converters.js'; import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; @@ -23,7 +23,7 @@ export class ApiFilterMastodon { public async getFilter() { try { - const data = await this.client.getFilter( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.getFilter( (this.request.params as any).id ); return convertFilter(data.data); } catch (e: any) { console.error(e); @@ -45,7 +45,7 @@ export class ApiFilterMastodon { public async updateFilter() { try { const body: any = this.request.body; - const data = await this.client.updateFilter(convertId((this.request.params as any).id, IdType.SharkeyId), body.pharse, body.context); + const data = await this.client.updateFilter((this.request.params as any).id, body.pharse, body.context); return convertFilter(data.data); } catch (e: any) { console.error(e); @@ -55,7 +55,7 @@ export class ApiFilterMastodon { public async rmFilter() { try { - const data = await this.client.deleteFilter( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.deleteFilter( (this.request.params as any).id ); return data.data; } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts index dc801dd05..c4628b58c 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts @@ -1,5 +1,4 @@ -import { IdConvertType as IdType, convertId, convertNotification } from '../converters.js'; -import { convertTimelinesArgsId } from './timeline.js'; +import { convertNotification } from '../converters.js'; import type { MegalodonInterface, Entity } from 'megalodon'; import type { FastifyRequest } from 'fastify'; @@ -19,7 +18,7 @@ export class ApiNotifyMastodon { public async getNotifications() { try { - const data = await this.client.getNotifications( convertTimelinesArgsId(toLimitToInt(this.request.query)) ); + const data = await this.client.getNotifications( toLimitToInt(this.request.query) ); const notifs = data.data; const processed = notifs.map((n: Entity.Notification) => { const convertedn = convertNotification(n); @@ -39,7 +38,7 @@ export class ApiNotifyMastodon { public async getNotification() { try { - const data = await this.client.getNotification( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.getNotification( (this.request.params as any).id ); const notif = convertNotification(data.data); if (notif.type !== 'follow' && notif.type !== 'follow_request' && notif.type === 'reaction') notif.type = 'favourite'; return notif; @@ -51,7 +50,7 @@ export class ApiNotifyMastodon { public async rmNotification() { try { - const data = await this.client.dismissNotification( convertId((this.request.params as any).id, IdType.SharkeyId) ); + const data = await this.client.dismissNotification( (this.request.params as any).id ); return data.data; } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts index 5c68402ed..d5839ff1c 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/search.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/search.ts @@ -1,6 +1,6 @@ import { Converter } from 'megalodon'; import { convertAccount, convertStatus } from '../converters.js'; -import { convertTimelinesArgsId, limitToInt } from './timeline.js'; +import { limitToInt } from './timeline.js'; import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; @@ -71,7 +71,7 @@ export class ApiSearchMastodon { public async SearchV1() { try { - const query: any = convertTimelinesArgsId(limitToInt(this.request.query as any)); + const query: any = limitToInt(this.request.query as any); const type = query.type || ''; const data = await this.client.search(query.q, { type: type, ...query }); return data.data; @@ -83,7 +83,7 @@ export class ApiSearchMastodon { public async SearchV2() { try { - const query: any = convertTimelinesArgsId(limitToInt(this.request.query as any)); + const query: any = limitToInt(this.request.query as any); const type = query.type; const acct = !type || type === 'accounts' ? await this.client.search(query.q, { type: 'accounts', ...query }) : null; const stat = !type || type === 'statuses' ? await this.client.search(query.q, { type: 'statuses', ...query }) : null; diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 2690a1036..a15e9761b 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -1,8 +1,8 @@ import querystring from 'querystring'; import { emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js'; -import { convertId, IdConvertType as IdType, convertAccount, convertAttachment, convertPoll, convertStatusSource, MastoConverters } from '../converters.js'; +import { convertAttachment, convertPoll, convertStatusSource, MastoConverters } from '../converters.js'; import { getClient } from '../MastodonApiServerService.js'; -import { convertTimelinesArgsId, limitToInt } from './timeline.js'; +import { limitToInt } from './timeline.js'; import type { Entity } from 'megalodon'; import type { FastifyInstance } from 'fastify'; import type { Config } from '@/config.js'; @@ -29,7 +29,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.getStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -44,8 +44,8 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getStatusSource(convertId(_request.params.id, IdType.SharkeyId)); - reply.send(convertStatusSource(data.data)); + const data = await client.getStatusSource(_request.params.id); + reply.send(data.data); } catch (e: any) { console.error(e); reply.code(_request.is404 ? 404 : 401).send(e.response.data); @@ -60,10 +60,7 @@ export class ApiStatusMastodon { const client = getClient(BASE_URL, accessTokens); const query: any = _request.query; try { - const data = await client.getStatusContext( - convertId(_request.params.id, IdType.SharkeyId), - convertTimelinesArgsId(limitToInt(query)), - ); + const data = await client.getStatusContext(_request.params.id, limitToInt(query)); data.data.ancestors = await Promise.all(data.data.ancestors.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))); data.data.descendants = await Promise.all(data.data.descendants.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))); reply.send(data.data); @@ -91,8 +88,8 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getStatusRebloggedBy(convertId(_request.params.id, IdType.SharkeyId)); - reply.send(data.data.map((account: Entity.Account) => convertAccount(account))); + const data = await client.getStatusRebloggedBy(_request.params.id); + reply.send(data.data.map((account: Entity.Account) => this.mastoconverter.convertAccount(account))); } catch (e: any) { console.error(e); reply.code(401).send(e.response.data); @@ -106,8 +103,8 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getStatusFavouritedBy(convertId(_request.params.id, IdType.SharkeyId)); - reply.send(data.data.map((account: Entity.Account) => convertAccount(account))); + const data = await client.getStatusFavouritedBy(_request.params.id); + reply.send(data.data.map((account: Entity.Account) => this.mastoconverter.convertAccount(account))); } catch (e: any) { console.error(e); reply.code(401).send(e.response.data); @@ -121,7 +118,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getMedia(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.getMedia(_request.params.id); reply.send(convertAttachment(data.data)); } catch (e: any) { console.error(e); @@ -136,7 +133,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getPoll(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.getPoll(_request.params.id); reply.send(convertPoll(data.data)); } catch (e: any) { console.error(e); @@ -152,7 +149,7 @@ export class ApiStatusMastodon { const client = getClient(BASE_URL, accessTokens); const body: any = _request.body; try { - const data = await client.votePoll(convertId(_request.params.id, IdType.SharkeyId), body.choices); + const data = await client.votePoll(_request.params.id, body.choices); reply.send(convertPoll(data.data)); } catch (e: any) { console.error(e); @@ -168,8 +165,6 @@ export class ApiStatusMastodon { const client = getClient(BASE_URL, accessTokens); let body: any = _request.body; try { - if (body.in_reply_to_id) body.in_reply_to_id = convertId(body.in_reply_to_id, IdType.SharkeyId); - if (body.quote_id) body.quote_id = convertId(body.quote_id, IdType.SharkeyId); if ( (!body.poll && body['poll[options][]']) || (!body.media_ids && body['media_ids[]']) @@ -201,9 +196,6 @@ export class ApiStatusMastodon { } if (!body.media_ids) body.media_ids = undefined; if (body.media_ids && !body.media_ids.length) body.media_ids = undefined; - if (body.media_ids) { - body.media_ids = (body.media_ids as string[]).map((p) => convertId(p, IdType.SharkeyId)); - } const { sensitive } = body; body.sensitive = typeof sensitive === 'string' ? sensitive === 'true' : sensitive; @@ -241,10 +233,7 @@ export class ApiStatusMastodon { try { if (!body.media_ids) body.media_ids = undefined; if (body.media_ids && !body.media_ids.length) body.media_ids = undefined; - if (body.media_ids) { - body.media_ids = (body.media_ids as string[]).map((p) => convertId(p, IdType.SharkeyId)); - } - const data = await client.editStatus(convertId(_request.params.id, IdType.SharkeyId), body); + const data = await client.editStatus(_request.params.id, body); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -259,10 +248,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = (await client.createEmojiReaction( - convertId(_request.params.id, IdType.SharkeyId), - '❤', - )) as any; + const data = (await client.createEmojiReaction(_request.params.id, '❤')) as any; reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -277,10 +263,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.deleteEmojiReaction( - convertId(_request.params.id, IdType.SharkeyId), - '❤', - ); + const data = await client.deleteEmojiReaction(_request.params.id, '❤'); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -295,7 +278,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.reblogStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.reblogStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -310,7 +293,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unreblogStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.unreblogStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -325,7 +308,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.bookmarkStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.bookmarkStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -340,7 +323,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unbookmarkStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.unbookmarkStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -355,7 +338,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.pinStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.pinStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -370,7 +353,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unpinStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.unpinStatus(_request.params.id); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -385,7 +368,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.createEmojiReaction(convertId(_request.params.id, IdType.SharkeyId), _request.params.name); + const data = await client.createEmojiReaction(_request.params.id, _request.params.name); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -400,7 +383,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.deleteEmojiReaction(convertId(_request.params.id, IdType.SharkeyId), _request.params.name); + const data = await client.deleteEmojiReaction(_request.params.id, _request.params.name); reply.send(await this.mastoconverter.convertStatus(data.data)); } catch (e: any) { console.error(e); @@ -415,7 +398,7 @@ export class ApiStatusMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.deleteStatus(convertId(_request.params.id, IdType.SharkeyId)); + const data = await client.deleteStatus(_request.params.id); reply.send(data.data); } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index e4f510ea2..b1b487f39 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -1,5 +1,5 @@ import { ParsedUrlQuery } from 'querystring'; -import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, MastoConverters } from '../converters.js'; +import { convertConversation, convertList, MastoConverters } from '../converters.js'; import { getClient } from '../MastodonApiServerService.js'; import type { Entity } from 'megalodon'; import type { FastifyInstance } from 'fastify'; @@ -32,13 +32,6 @@ export function argsToBools(q: ParsedUrlQuery) { return q; } -export function convertTimelinesArgsId(q: ParsedUrlQuery) { - if (typeof q.min_id === 'string') q.min_id = convertId(q.min_id, IdType.SharkeyId); - if (typeof q.max_id === 'string') q.max_id = convertId(q.max_id, IdType.SharkeyId); - if (typeof q.since_id === 'string') q.since_id = convertId(q.since_id, IdType.SharkeyId); - return q; -} - export class ApiTimelineMastodon { private fastify: FastifyInstance; private mastoconverter: MastoConverters; @@ -56,8 +49,8 @@ export class ApiTimelineMastodon { try { const query: any = _request.query; const data = query.local === 'true' - ? await client.getLocalTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query)))) - : await client.getPublicTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query)))); + ? await client.getLocalTimeline(argsToBools(limitToInt(query))) + : await client.getPublicTimeline(argsToBools(limitToInt(query))); reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status)))); } catch (e: any) { console.error(e); @@ -74,7 +67,7 @@ export class ApiTimelineMastodon { const client = getClient(BASE_URL, accessTokens); try { const query: any = _request.query; - const data = await client.getHomeTimeline(convertTimelinesArgsId(limitToInt(query))); + const data = await client.getHomeTimeline(limitToInt(query)); reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status)))); } catch (e: any) { console.error(e); @@ -92,7 +85,7 @@ export class ApiTimelineMastodon { try { const query: any = _request.query; const params: any = _request.params; - const data = await client.getTagTimeline(params.hashtag, convertTimelinesArgsId(limitToInt(query))); + const data = await client.getTagTimeline(params.hashtag, limitToInt(query)); reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status)))); } catch (e: any) { console.error(e); @@ -110,7 +103,7 @@ export class ApiTimelineMastodon { try { const query: any = _request.query; const params: any = _request.params; - const data = await client.getListTimeline(convertId(params.id, IdType.SharkeyId), convertTimelinesArgsId(limitToInt(query))); + const data = await client.getListTimeline(params.id, limitToInt(query)); reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status)))); } catch (e: any) { console.error(e); @@ -127,7 +120,7 @@ export class ApiTimelineMastodon { const client = getClient(BASE_URL, accessTokens); try { const query: any = _request.query; - const data = await client.getConversationTimeline(convertTimelinesArgsId(limitToInt(query))); + const data = await client.getConversationTimeline(limitToInt(query)); reply.send(data.data.map((conversation: Entity.Conversation) => convertConversation(conversation))); } catch (e: any) { console.error(e); @@ -144,7 +137,7 @@ export class ApiTimelineMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const params: any = _request.params; - const data = await client.getList(convertId(params.id, IdType.SharkeyId)); + const data = await client.getList(params.id); reply.send(convertList(data.data)); } catch (e: any) { console.error(e); @@ -178,11 +171,8 @@ export class ApiTimelineMastodon { const client = getClient(BASE_URL, accessTokens); const params: any = _request.params; const query: any = _request.query; - const data = await client.getAccountsInList( - convertId(params.id, IdType.SharkeyId), - convertTimelinesArgsId(query), - ); - reply.send(data.data.map((account: Entity.Account) => convertAccount(account))); + const data = await client.getAccountsInList(params.id, query); + reply.send(data.data.map((account: Entity.Account) => this.mastoconverter.convertAccount(account))); } catch (e: any) { console.error(e); console.error(e.response.data); @@ -199,10 +189,7 @@ export class ApiTimelineMastodon { const client = getClient(BASE_URL, accessTokens); const params: any = _request.params; const query: any = _request.query; - const data = await client.addAccountsToList( - convertId(params.id, IdType.SharkeyId), - (query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)), - ); + const data = await client.addAccountsToList(params.id, query.accounts_id); reply.send(data.data); } catch (e: any) { console.error(e); @@ -220,10 +207,7 @@ export class ApiTimelineMastodon { const client = getClient(BASE_URL, accessTokens); const params: any = _request.params; const query: any = _request.query; - const data = await client.deleteAccountsFromList( - convertId(params.id, IdType.SharkeyId), - (query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)), - ); + const data = await client.deleteAccountsFromList(params.id, query.accounts_id); reply.send(data.data); } catch (e: any) { console.error(e); @@ -258,7 +242,7 @@ export class ApiTimelineMastodon { const client = getClient(BASE_URL, accessTokens); const body: any = _request.body; const params: any = _request.params; - const data = await client.updateList(convertId(params.id, IdType.SharkeyId), body.title); + const data = await client.updateList(params.id, body.title); reply.send(convertList(data.data)); } catch (e: any) { console.error(e); @@ -275,7 +259,7 @@ export class ApiTimelineMastodon { const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const params: any = _request.params; - const data = await client.deleteList(convertId(params.id, IdType.SharkeyId)); + const data = await client.deleteList(params.id); reply.send(data.data); } catch (e: any) { console.error(e); diff --git a/packages/megalodon/package.json b/packages/megalodon/package.json index ebd958834..3bee6b8ae 100644 --- a/packages/megalodon/package.json +++ b/packages/megalodon/package.json @@ -64,15 +64,15 @@ "socks-proxy-agent": "^8.0.2", "typescript": "5.1.6", "uuid": "^9.0.1", - "ws": "8.14.2" - }, - "devDependencies": { + "ws": "8.14.2", "@types/core-js": "^2.5.6", "@types/form-data": "^2.5.0", "@types/jest": "^29.5.5", "@types/object-assign-deep": "^0.4.1", "@types/parse-link-header": "^2.0.1", - "@types/uuid": "^9.0.4", + "@types/uuid": "^9.0.4" + }, + "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "eslint": "^8.49.0", diff --git a/packages/megalodon/src/entities/account.ts b/packages/megalodon/src/entities/account.ts index 89c0f17c4..e2219dd04 100644 --- a/packages/megalodon/src/entities/account.ts +++ b/packages/megalodon/src/entities/account.ts @@ -5,15 +5,16 @@ namespace Entity { export type Account = { id: string + fqn?: string username: string acct: string display_name: string locked: boolean discoverable?: boolean - group: boolean | null - noindex: boolean | null - suspended: boolean | null - limited: boolean | null + group?: boolean | null + noindex?: boolean | null + suspended?: boolean | null + limited?: boolean | null created_at: string followers_count: number following_count: number diff --git a/packages/megalodon/src/entities/status.ts b/packages/megalodon/src/entities/status.ts index 8842981eb..da36a0471 100644 --- a/packages/megalodon/src/entities/status.ts +++ b/packages/megalodon/src/entities/status.ts @@ -17,7 +17,7 @@ namespace Entity { in_reply_to_account_id: string | null reblog: Status | null content: string - plain_content: string | null + plain_content?: string | null created_at: string emojis: Emoji[] replies_count: number diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts index de2b6e2e7..a8c7c44d0 100644 --- a/packages/megalodon/src/misskey.ts +++ b/packages/megalodon/src/misskey.ts @@ -2017,7 +2017,7 @@ export default class Misskey implements MegalodonInterface { } if (options.exclude_type) { params = Object.assign(params, { - excludeType: options.exclude_type.map(e => MisskeyAPI.Converter.encodeNotificationType(e)) + excludeTypes: options.exclude_type.map(e => MisskeyAPI.Converter.encodeNotificationType(e)) }) } } diff --git a/packages/megalodon/src/misskey/api_client.ts b/packages/megalodon/src/misskey/api_client.ts index c30886f90..66347fc46 100644 --- a/packages/megalodon/src/misskey/api_client.ts +++ b/packages/megalodon/src/misskey/api_client.ts @@ -78,8 +78,10 @@ namespace MisskeyAPI { acct = `${u.username}@${u.host}`; acctUrl = `https://${u.host}/@${u.username}`; } + const fqn = `${u.username}@${u.host ?? host}`; return { id: u.id, + fqn: fqn, username: u.username, acct: acct, display_name: u.name ? u.name : '', @@ -465,8 +467,8 @@ namespace MisskeyAPI { export const stats = (s: Entity.Stats): MegalodonEntity.Stats => { return { - user_count: s.usersCount, - status_count: s.notesCount, + user_count: s.originalUsersCount, + status_count: s.originalNotesCount, domain_count: s.instances } }