From 3d8930f07006fac3e8e5caff2118d057dbbc987c Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Fri, 21 Mar 2025 23:26:12 -0400 Subject: [PATCH] implement /api/v1/favourites --- .../api/mastodon/MastodonApiServerService.ts | 18 +++++- packages/megalodon/src/misskey.ts | 62 +++++++++++-------- packages/megalodon/src/misskey/api_client.ts | 1 + .../src/misskey/entities/reaction.ts | 5 ++ 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts index 94345027ed..517beb4f44 100644 --- a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts +++ b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts @@ -19,6 +19,7 @@ import { ApiStatusMastodon } from '@/server/api/mastodon/endpoints/status.js'; import { ApiNotificationsMastodon } from '@/server/api/mastodon/endpoints/notifications.js'; import { ApiTimelineMastodon } from '@/server/api/mastodon/endpoints/timeline.js'; import { ApiSearchMastodon } from '@/server/api/mastodon/endpoints/search.js'; +import { ApiError } from '@/server/api/error.js'; import { parseTimelineArgs, TimelineArgs, toBoolean } from './argsUtils.js'; import { convertAnnouncement, convertAttachment, MastoConverters, convertRelationship } from './converters.js'; import type { Entity } from 'megalodon'; @@ -231,8 +232,21 @@ export class MastodonApiServerService { fastify.get<{ Querystring: TimelineArgs }>('/v1/favourites', async (_request, reply) => { const { client, me } = await this.clientService.getAuthClient(_request); - const data = await client.getFavourites(parseTimelineArgs(_request.query)); - const response = Promise.all(data.data.map((status) => this.mastoConverters.convertStatus(status, me))); + if (!me) { + throw new ApiError({ + message: 'Credential required.', + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + httpStatusCode: 401, + }); + } + + const args = { + ...parseTimelineArgs(_request.query), + userId: me.id, + }; + const data = await client.getFavourites(args); + const response = await Promise.all(data.data.map((status) => this.mastoConverters.convertStatus(status, me))); reply.send(response); }); diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts index 643082dff0..dce0fb21b7 100644 --- a/packages/megalodon/src/misskey.ts +++ b/packages/megalodon/src/misskey.ts @@ -691,36 +691,48 @@ export default class Misskey implements MegalodonInterface { }) } + /** + * POST /api/users/reactions + */ + public async getReactions(userId: string, options?: { limit?: number; max_id?: string; min_id?: string }): Promise> { + let params = { + userId, + }; + if (options) { + if (options.limit) { + params = Object.assign(params, { + limit: options.limit + }) + } + if (options.max_id) { + params = Object.assign(params, { + untilId: options.max_id + }) + } + if (options.min_id) { + params = Object.assign(params, { + sinceId: options.min_id + }) + } + } + return this.client.post('/api/users/reactions', params); + } + // ====================================== // accounts/favourites // ====================================== /** - * POST /api/i/favorites + * POST /api/users/reactions */ - public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - untilId: options.max_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - sinceId: options.min_id - }) - } - } - return this.client.post>('/api/i/favorites', params).then(res => { - return Object.assign(res, { - data: res.data.map(fav => MisskeyAPI.Converter.note(fav.note, this.baseUrl)) - }) - }) + public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string; userId?: string }): Promise>> { + const userId = options?.userId ?? (await this.verifyAccountCredentials()).data.id; + + const response = await this.getReactions(userId, options); + + return { + ...response, + data: response.data.map(r => MisskeyAPI.Converter.note(r.note, this.baseUrl)), + }; } // ====================================== diff --git a/packages/megalodon/src/misskey/api_client.ts b/packages/megalodon/src/misskey/api_client.ts index a4352613eb..a9a592b28c 100644 --- a/packages/megalodon/src/misskey/api_client.ts +++ b/packages/megalodon/src/misskey/api_client.ts @@ -32,6 +32,7 @@ namespace MisskeyAPI { export type Notification = MisskeyEntity.Notification export type Poll = MisskeyEntity.Poll export type Reaction = MisskeyEntity.Reaction + export type NoteReaction = MisskeyEntity.NoteReaction export type Relation = MisskeyEntity.Relation export type User = MisskeyEntity.User export type UserDetail = MisskeyEntity.UserDetail diff --git a/packages/megalodon/src/misskey/entities/reaction.ts b/packages/megalodon/src/misskey/entities/reaction.ts index 270ca6eab1..de959b2627 100644 --- a/packages/megalodon/src/misskey/entities/reaction.ts +++ b/packages/megalodon/src/misskey/entities/reaction.ts @@ -1,4 +1,5 @@ /// +/// namespace MisskeyEntity { export type Reaction = { @@ -7,4 +8,8 @@ namespace MisskeyEntity { user: User type: string } + + export type NoteReaction = Reaction & { + note: Note + } }