diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts b/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts index f98c6c929f..a9bcf69351 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts @@ -33,7 +33,7 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { - if (ps.key !== "reactions") return; + if (ps.key !== "reactions" && ps.key !== "defaultNoteVisibility") return; const query = RegistryItems.createQueryBuilder("item") .where("item.domain IS NULL") .andWhere("item.userId = :userId", { userId: user.id }) diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index 29cfbf93c0..9e8c458868 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -112,7 +112,7 @@ mastoFileRouter.post("/v2/media", upload.single("file"), async (ctx) => { ctx.status = 401; return; } - const data = await client.uploadMedia(multipartData); + const data = await client.uploadMedia(multipartData, ctx.request.body); ctx.body = convertAttachment(data.data as Entity.Attachment); } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 3fb1c9cbe4..5d0abdedd6 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -48,7 +48,7 @@ export function apiAccountMastodon(router: Router): void { acct.source = { note: acct.note, fields: acct.fields, - privacy: "public", + privacy: await client.getDefaultPostPrivacy(), sensitive: false, language: "", }; diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 3c58cf3a42..76057ef0a2 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -123,27 +123,7 @@ export function apiStatusMastodon(router: Router): void { id, convertTimelinesArgsId(limitToInt(ctx.query as any)), ); - const status = await client.getStatus(id); - let reqInstance = axios.create({ - headers: { - Authorization: ctx.headers.authorization, - }, - }); - const reactionsAxios = await reqInstance.get( - `${BASE_URL}/api/notes/reactions?noteId=${id}`, - ); - const reactions: IReaction[] = reactionsAxios.data; - const text = reactions - .map((r) => `${r.type.replace("@.", "")} ${r.user.username}`) - .join("
"); - data.data.descendants.unshift( - statusModel( - status.data.id, - status.data.account.id, - status.data.emojis, - text, - ), - ); + data.data.ancestors = data.data.ancestors.map((status) => convertStatus(status), ); @@ -456,65 +436,3 @@ async function getFirstReaction( return react; } } - -export function statusModel( - id: string | null, - acctId: string | null, - emojis: MastodonEntity.Emoji[], - content: string, -) { - const now = new Date().toISOString(); - return { - id: "9atm5frjhb", - uri: "/static-assets/transparent.png", // "" - url: "/static-assets/transparent.png", // "", - account: { - id: "9arzuvv0sw", - username: "Reactions", - acct: "Reactions", - display_name: "Reactions to this post", - locked: false, - created_at: now, - followers_count: 0, - following_count: 0, - statuses_count: 0, - note: "", - url: "/static-assets/transparent.png", - avatar: "/static-assets/badges/info.png", - avatar_static: "/static-assets/badges/info.png", - header: "/static-assets/transparent.png", // "" - header_static: "/static-assets/transparent.png", // "" - emojis: [], - fields: [], - moved: null, - bot: false, - }, - in_reply_to_id: id, - in_reply_to_account_id: acctId, - reblog: null, - content: `

${content}

`, - plain_content: null, - created_at: now, - emojis: emojis, - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - favourited: false, - reblogged: false, - muted: false, - sensitive: false, - spoiler_text: "", - visibility: "public" as const, - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: null, - language: null, - pinned: false, - emoji_reactions: [], - bookmarked: false, - quote: null, - }; -} diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts index f2befd31e3..e05a12eaad 100644 --- a/packages/megalodon/src/misskey.ts +++ b/packages/megalodon/src/misskey.ts @@ -460,7 +460,7 @@ export default class Misskey implements MegalodonInterface { if (options) { if (options.limit) { params = Object.assign(params, { - limit: options.limit + limit: options.limit <= 100 ? options.limit : 100 }) } else { @@ -474,11 +474,11 @@ export default class Misskey implements MegalodonInterface { limit: 40 }) } - return this.client.post>('/api/users/followers', params).then(res => { - return Object.assign(res, { - data: res.data.map(f => this.converter.follower(f)) - }) - }) + return this.client.post>('/api/users/followers', params).then(async res => { + return Object.assign(res, { + data: (await Promise.all(res.data.map(async f => (this.getAccount(f.followerId)).then(p => p.data)))) + }) + }) } /** @@ -498,15 +498,15 @@ export default class Misskey implements MegalodonInterface { if (options) { if (options.limit) { params = Object.assign(params, { - limit: options.limit + limit: options.limit <= 100 ? options.limit : 100 }) } } - return this.client.post>('/api/users/following', params).then(res => { - return Object.assign(res, { - data: res.data.map(f => this.converter.following(f)) - }) - }) + return this.client.post>('/api/users/following', params).then(async res => { + return Object.assign(res, { + data: (await Promise.all(res.data.map(async f => (this.getAccount(f.followeeId)).then(p => p.data)))) + }) + }) } public async getAccountLists(_id: string): Promise>> { @@ -1079,23 +1079,11 @@ export default class Misskey implements MegalodonInterface { // accounts/preferences // ====================================== public async getPreferences(): Promise> { - return this.client.post('/api/i').then(res => { - /* - return this.client.post('/api/i/registry/get-all', { - scope: ['client', 'base'], - }).then(ga => { - return Object.assign(res, { - data: this.converter.userPreferences(res.data, ga.data) - }) - }) - */ - - // TODO: - // FIXME: get this from api - return Object.assign(res, { - data: this.converter.userPreferences(res.data, {defaultNoteVisibility: "followers", tutorial: -1}) - }) - }) + return this.client.post('/api/i').then(async res => { + return Object.assign(res, { + data: this.converter.userPreferences(res.data, await this.getDefaultPostPrivacy()) + }) + }) } // ====================================== @@ -1529,6 +1517,23 @@ export default class Misskey implements MegalodonInterface { .then(res => res.data[0] ?? '⭐'); } + private async getDefaultPostPrivacy(): Promise<'public' | 'unlisted' | 'private' | 'direct'> { + // NOTE: get-unsecure is calckey's extension. + // Misskey doesn't have this endpoint and regular `/i/registry/get` won't work + // unless you have a 'nativeToken', which is reserved for the frontend webapp. + + return this.client + .post('/api/i/registry/get-unsecure', { + key: 'defaultNoteVisibility', + scope: ['client', 'base'], + }) + .then(res => { + if (!res.data || (res.data != 'public' && res.data != 'home' && res.data != 'followers' && res.data != 'specified')) + return 'public'; + return this.converter.visibility(res.data); + }); + } + public async unfavouriteStatus(id: string): Promise> { // NOTE: Misskey allows only one reaction per status, so we don't need to care what that emoji was. return this.deleteEmojiReaction(id, ''); @@ -1638,20 +1643,26 @@ export default class Misskey implements MegalodonInterface { /** * POST /api/drive/files/create */ - public async uploadMedia(file: any, _options?: { description?: string; focus?: string }): Promise> { + public async uploadMedia(file: any, options?: { description?: string; focus?: string }): Promise> { const formData = new FormData() - formData.append('file', fs.createReadStream(file.path), { - contentType: file.mimetype, - filename: file.originalname, - }) + formData.append('file', fs.createReadStream(file.path), { + contentType: file.mimetype + }) + + if (file.originalname != null && file.originalname !== 'file') + formData.append('name', file.originalname); + + if (options?.description != null) + formData.append('comment', options.description); + let headers: { [key: string]: string } = {} if (typeof formData.getHeaders === 'function') { headers = formData.getHeaders() } return this.client .post('/api/drive/files/create', formData, headers) - .then(res => ({ ...res, data: this.converter.file(res.data) })) - } + .then(res => ({ ...res, data: this.converter.file(res.data) })) + } public async getMedia(id: string): Promise> { const res = await this.client.post('/api/drive/files/show', { fileId: id }) @@ -1679,6 +1690,12 @@ export default class Misskey implements MegalodonInterface { isSensitive: options.is_sensitive }) } + + if (options.description !== undefined) { + params = Object.assign(params, { + comment: options.description + }) + } } return this.client .post('/api/drive/files/update', params) diff --git a/packages/megalodon/src/misskey/api_client.ts b/packages/megalodon/src/misskey/api_client.ts index 7f1409c706..34e11784e0 100644 --- a/packages/megalodon/src/misskey/api_client.ts +++ b/packages/megalodon/src/misskey/api_client.ts @@ -134,8 +134,8 @@ namespace MisskeyAPI { url: acctUrl, avatar: u.avatarUrl, avatar_static: u.avatarUrl, - header: this.plcUrl, // FIXME - header_static: this.plcUrl, // FIXME + header: this.plcUrl, + header_static: this.plcUrl, emojis: u.emojis.map(e => this.emoji(e)), moved: null, fields: [], @@ -174,13 +174,13 @@ namespace MisskeyAPI { } } - userPreferences = (u: MisskeyAPI.Entity.UserDetailMe, g: MisskeyAPI.Entity.GetAll): MegalodonEntity.Preferences => { + userPreferences = (u: MisskeyAPI.Entity.UserDetailMe, v: 'public' | 'unlisted' | 'private' | 'direct'): MegalodonEntity.Preferences => { return { "reading:expand:media": "default", "reading:expand:spoilers": false, "posting:default:language": u.lang, "posting:default:sensitive": u.alwaysMarkNsfw, - "posting:default:visibility": this.visibility(g.defaultNoteVisibility) + "posting:default:visibility": v } } @@ -308,7 +308,7 @@ namespace MisskeyAPI { emojis: n.emojis.map(e => this.emoji(e)), replies_count: n.repliesCount, reblogs_count: n.renoteCount, - favourites_count: this.getTotalReactions(n.reactions), // FIXME: instead get # of default reaction emoji reactions + favourites_count: this.getTotalReactions(n.reactions), reblogged: false, favourited: !!n.myReaction, muted: false,