From c1198e3420f41c375e4c54c7874267fb9370ffca Mon Sep 17 00:00:00 2001 From: naskya <m@naskya.net> Date: Sun, 30 Jun 2024 12:36:30 +0900 Subject: [PATCH] docs: list available Mastodon API endpoints/methods --- docs/api-change.md | 123 ++++++++++++++++++ .../server/api/mastodon/endpoints/account.ts | 2 +- .../src/server/api/mastodon/endpoints/list.ts | 8 +- .../api/mastodon/endpoints/notifications.ts | 2 +- .../server/api/mastodon/endpoints/timeline.ts | 14 +- 5 files changed, 136 insertions(+), 13 deletions(-) diff --git a/docs/api-change.md b/docs/api-change.md index cc70b9dcf9..c9bc7fde37 100644 --- a/docs/api-change.md +++ b/docs/api-change.md @@ -8,6 +8,129 @@ Breaking changes are indicated by the :warning: icon. - :warning: The new API uses a new format to manage Mastodon sessions in the database, whereas old implementation uses Misskey sessions. All previous client app and token registrations will not work with the new API. All clients need to be re-registered and all users need to re-authenticate. - :warning: All IDs (of statuses/notes, notifications, users, etc.) will be using the alphanumerical format, aligning with the Firefish/Misskey API. The old numerical IDs will not work when queried against the new API. +<details> + +<summary>Available endpoints (under <code>https://instance-domain/api/</code>)</summary> + +- `POST oauth/token` +- `POST oauth/revoke` +- `POST v1/apps` +- `GET v1/apps/verify_credentials` +- `POST v1/firefish/apps/info` (Firefish extension, uses MiAuth) +- `POST v1/firefish/auth/code` (Firefish extension, uses MiAuth) + +- `GET v1/accounts/verify_credentials` +- `PATCH v1/accounts/update_credentials` +- `GET v1/accounts/lookup` +- `GET v1/accounts/relationships` +- `GET v1/accounts/search` +- `GET v1/accounts/:id` +- `GET v1/accounts/:id/statuses` +- `GET v1/accounts/:id/featured_tags` +- `GET v1/accounts/:id/followers` +- `GET v1/accounts/:id/following` +- `GET v1/accounts/:id/lists` +- `POST v1/accounts/:id/follow` +- `POST v1/accounts/:id/unfollow` +- `POST v1/accounts/:id/block` +- `POST v1/accounts/:id/unblock` +- `POST v1/accounts/:id/mute` +- `POST v1/accounts/:id/unmute` + +- `GET v1/featured_tags` (returns an empty list) +- `GET v1/followed_tags` (returns an empty list) +- `GET v1/bookmarks` +- `GET v1/favourites` + +- `GET v1/mutes` +- `GET v1/blocks` +- `GET v1/follow_requests` +- `POST v1/follow_requests/:id/authorize` +- `POST v1/follow_requests/:id/reject` + +- `GET v1/filters` +- `POST v1/filters` +- `GET v2/filters` +- `POST v2/filters` + +- `GET v1/lists` +- `POST v1/lists` +- `GET v1/lists/:id` +- `PUT v1/lists/:id` +- `DELETE v1/lists/:id` +- `GET v1/lists/:id/accounts` +- `POST v1/lists/:id/accounts` +- `DELETE v1/lists/:id/accounts` + +- `GET v1/media/:id` +- `PUT v1/media/:id` +- `POST v1/media` +- `POST v2/media` + +- `GET v1/custom_emojis` +- `GET v1/instance` +- `GET v2/instance` +- `GET v1/announcements` +- `POST v1/announcements/:id/dismiss` +- `GET v1/trends` (pagination is unimplemented) +- `GET v1/trends/tags` (pagination is unimplemented) +- `GET v1/trends/statuses` +- `GET v1/trends/links` (returns an empty list) +- `GET v1/preferences` +- `GET v2/suggestions` + +- `GET v1/notifications` +- `GET v1/notifications/:id` +- `POST v1/notifications/clear` +- `POST v1/notifications/:id/dismiss` +- `POST v1/conversations/:id/read` +- `GET v1/push/subscription` +- `POST v1/push/subscription` +- `DELETE v1/push/subscription` + +- `GET v1/search` +- `GET v2/search` + +- `POST v1/statuses` +- `PUT v1/statuses/:id` +- `GET v1/statuses/:id` +- `DELETE v1/statuses/:id` +- `GET v1/statuses/:id/context` +- `GET v1/statuses/:id/history` +- `GET v1/statuses/:id/source` +- `GET v1/statuses/:id/reblogged_by` +- `GET v1/statuses/:id/favourited_by` +- `POST v1/statuses/:id/favourite` +- `POST v1/statuses/:id/unfavourite` +- `POST v1/statuses/:id/reblog` +- `POST v1/statuses/:id/unreblog` +- `POST v1/statuses/:id/bookmark` +- `POST v1/statuses/:id/unbookmark` +- `POST v1/statuses/:id/pin` +- `POST v1/statuses/:id/unpin` +- `POST v1/statuses/:id/react/:name` +- `POST v1/statuses/:id/unreact/:name` +- `POST v1/statuses/:id/translate` + +- `GET v1/polls/:id` +- `POST v1/polls/:id/votes` + +- `GET v1/scheduled_statuses` +- `GET v1/scheduled_statuses/:id` (reschedule (`PUT` method) is unimplemented) +- `DELETE v1/scheduled_statuses/:id` + +- `GET v1/streaming/health` + +- `GET v1/timelines/public` +- `GET v1/timelines/tag/:hashtag` +- `GET v1/timelines/home` +- `GET v1/timelines/list/:listId` +- `GET v1/conversations` +- `GET v1/markers` +- `POST v1/markers` + +</details> + ## v20240607 - `GET` request is now allowed for the `latest-version` endpoint. diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 25cf3ac1da..c835830db3 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -34,7 +34,7 @@ export function setupEndpointsAccount(router: Router): void { async (ctx) => { const ids = normalizeUrlQuery(ctx.query, ["id[]"])["id[]"] ?? - normalizeUrlQuery(ctx.query, ["id"])["id"] ?? + normalizeUrlQuery(ctx.query, ["id"]).id ?? []; ctx.body = await UserHelpers.getUserRelationhipToMany(ids, ctx.user.id); }, diff --git a/packages/backend/src/server/api/mastodon/endpoints/list.ts b/packages/backend/src/server/api/mastodon/endpoints/list.ts index e35c9fe316..ea06b10ad5 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/list.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/list.ts @@ -12,17 +12,17 @@ import { auth } from "@/server/api/mastodon/middleware/auth.js"; import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js"; export function setupEndpointsList(router: Router): void { - router.get("/v1/lists", auth(true, ["read:lists"]), async (ctx, reply) => { + router.get("/v1/lists", auth(true, ["read:lists"]), async (ctx, _reply) => { ctx.body = await ListHelpers.getLists(ctx); }); router.get<{ Params: { id: string } }>( "/v1/lists/:id", auth(true, ["read:lists"]), - async (ctx, reply) => { + async (ctx, _reply) => { ctx.body = await ListHelpers.getListOr404(ctx.params.id, ctx); }, ); - router.post("/v1/lists", auth(true, ["write:lists"]), async (ctx, reply) => { + router.post("/v1/lists", auth(true, ["write:lists"]), async (ctx, _reply) => { const body = ctx.request.body as any; const title = (body.title ?? "").trim(); ctx.body = await ListHelpers.createList(title, ctx); @@ -46,7 +46,7 @@ export function setupEndpointsList(router: Router): void { router.delete<{ Params: { id: string } }>( "/v1/lists/:id", auth(true, ["write:lists"]), - async (ctx, reply) => { + async (ctx, _reply) => { const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id, diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts index 8a6f079af0..2164c4f04b 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts @@ -67,7 +67,7 @@ export function setupEndpointsNotifications(router: Router): void { router.post( "/v1/conversations/:id/read", auth(true, ["write:conversations"]), - async (ctx, reply) => { + async (ctx, _reply) => { await NotificationHelpers.markConversationAsRead(ctx.params.id, ctx); ctx.body = {}; }, diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index 05373c3802..b651731a04 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -67,7 +67,7 @@ export function setupEndpointsTimeline(router: Router): void { "/v1/timelines/public", auth(true, ["read:statuses"]), filterContext("public"), - async (ctx, reply) => { + async (ctx, _reply) => { const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query))); const res = await TimelineHelpers.getPublicTimeline( args.max_id, @@ -87,7 +87,7 @@ export function setupEndpointsTimeline(router: Router): void { "/v1/timelines/tag/:hashtag", auth(false, ["read:statuses"]), filterContext("public"), - async (ctx, reply) => { + async (ctx, _reply) => { const tag = (ctx.params.hashtag ?? "").trim().toLowerCase(); const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)), [ "any[]", @@ -116,7 +116,7 @@ export function setupEndpointsTimeline(router: Router): void { "/v1/timelines/home", auth(true, ["read:statuses"]), filterContext("home"), - async (ctx, reply) => { + async (ctx, _reply) => { const args = normalizeUrlQuery(limitToInt(ctx.query)); const res = await TimelineHelpers.getHomeTimeline( args.max_id, @@ -133,7 +133,7 @@ export function setupEndpointsTimeline(router: Router): void { "/v1/timelines/list/:listId", auth(true, ["read:lists"]), filterContext("home"), - async (ctx, reply) => { + async (ctx, _reply) => { const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.listId, @@ -156,7 +156,7 @@ export function setupEndpointsTimeline(router: Router): void { router.get( "/v1/conversations", auth(true, ["read:statuses"]), - async (ctx, reply) => { + async (ctx, _reply) => { const args = normalizeUrlQuery(limitToInt(ctx.query)); ctx.body = await TimelineHelpers.getConversations( args.max_id, @@ -171,7 +171,7 @@ export function setupEndpointsTimeline(router: Router): void { router.get( "/v1/markers", auth(true, ["read:statuses"]), - async (ctx, reply) => { + async (ctx, _reply) => { const args = normalizeUrlQuery(ctx.query, ["timeline[]"]); ctx.body = await TimelineHelpers.getMarkers(args["timeline[]"], ctx); }, @@ -180,7 +180,7 @@ export function setupEndpointsTimeline(router: Router): void { router.post( "/v1/markers", auth(true, ["write:statuses"]), - async (ctx, reply) => { + async (ctx, _reply) => { const body = ctx.request.body; ctx.body = await TimelineHelpers.setMarkers(body, ctx); },